# LibreChat Documentation

> Full text of the LibreChat documentation for language models and coding agents. Pages follow the docs navigation order: start with deployment and configuration, then features and tools, and finish with the contributing guides.

# Documentation (https://www.librechat.ai/docs)

<DocsHub />

<Callout type="info" title="Looking for a desktop installer?">

LibreChat is a self-hosted web application, not a native Windows app or Linux AppImage. On Windows, use [Docker Desktop](/docs/local/docker) for the simplest local setup. For Linux desktops or servers, use [Docker](/docs/local/docker), [npm](/docs/local/npm), or a [remote hosting guide](/docs/remote).

</Callout>


# Quick Start (https://www.librechat.ai/docs/quick_start)

<QuickStartHub />


# Local Setup Guide (https://www.librechat.ai/docs/quick_start/local_setup)

import AdditionalLinks from '@/components/repeated/AdditionalLinks.mdx';

This is a condensed version of our [Local Installation Guide](/docs/local/)

## Step 1. Download the Project

### Manual Download

1. **Go to the Project Page**: Visit [https://github.com/danny-avila/LibreChat](https://github.com/danny-avila/LibreChat).

2. **Download the ZIP File**: Click the green "Code" button, then click "Download ZIP."

3. **Extract the ZIP File**: Find the downloaded ZIP file, right-click, and select "Extract All...".

### Using Git

Run the following [git](https://git-scm.com/) command in your terminal, from the desired parent directory:

```bash
git clone https://github.com/danny-avila/LibreChat.git
```

## Step 2. Install Docker

1. **Download**: Go to [Docker Desktop Download Page](https://www.docker.com/products/docker-desktop) and download Docker Desktop.
2. **Install**: Open the installer and follow the instructions.
3. **Run**: Open Docker Desktop to ensure it is running.

**Notes:**
- Docker Desktop is recommended for most users. If you are looking for an advanced docker/container setup, especially for a remote server installation, see our [Ubuntu Docker Deployment Guide](/docs/remote/docker_linux).
- You may need to restart your computer after installation.

## Step 3. Run the App

1. **Navigate to the Project Directory**

2. **Create and Configure .env File**:
   - Copy the contents of `.env.example` to a new file named `.env`.
   - Fill in any necessary values.
      - For an in-depth environment configuration, see the [.env File Configuration Guide](/docs/configuration/dotenv).

3. **Start the Application**:
   - Run the following command:

   ```bash
   docker compose up -d
   ```

## Conclusion

**That's it!** You should now have **LibreChat** running locally on your machine. Enjoy!

---

<AdditionalLinks />


# Custom Endpoints (https://www.librechat.ai/docs/quick_start/custom_endpoints)

LibreChat supports OpenAI API-compatible services as custom endpoints. It also supports Anthropic-compatible custom endpoints with `provider: "anthropic"`. You configure endpoints in `librechat.yaml`, store API keys in `.env`, and mount the config via `docker-compose.override.yml` for Docker deployments.

<Callout type="info" title="Which File Does What?">

Custom endpoint setup involves three files, each with a specific role:

1. **`librechat.yaml`** -- Defines your custom endpoints (name, API URL, models, display settings)
2. **`.env`** -- Stores sensitive values like API keys (referenced from librechat.yaml using `${VAR_NAME}` syntax)
3. **`docker-compose.override.yml`** -- Mounts `librechat.yaml` into the Docker container (Docker users only)

For a full overview of how these files work together, see the [Configuration Overview](/docs/configuration).

</Callout>

<Callout type="warning" title="Before You Start">

This guide assumes you have LibreChat installed and running. If not, complete the [Docker setup](/docs/local/docker) first.

</Callout>

## Step 1. Mount librechat.yaml (Docker Only)

Docker users need to mount `librechat.yaml` as a volume so the container can read it. Skip this step if you are running LibreChat locally without Docker.

```bash
cp docker-compose.override.yml.example docker-compose.override.yml
```

Edit `docker-compose.override.yml` and ensure the volume mount is uncommented:

```yaml filename="docker-compose.override.yml"
services:
  api:
    volumes:
      - type: bind
        source: ./librechat.yaml
        target: /app/librechat.yaml
```

Learn more: [Docker Override Guide](/docs/configuration/docker_override)

## Step 2. Configure librechat.yaml

Create a `librechat.yaml` file in the project root (if it does not exist) and add your endpoint configuration. See the [librechat.yaml guide](/docs/configuration/librechat_yaml) for detailed setup instructions.

Here is an example with **OpenRouter**, **Ollama**, and an Anthropic-compatible gateway:

```yaml filename="librechat.yaml"
version: 1.3.13
cache: true
endpoints:
  custom:
    - name: 'OpenRouter'
      apiKey: '${OPENROUTER_KEY}'
      baseURL: 'https://openrouter.ai/api/v1'
      models:
        default: ['meta-llama/llama-3-70b-instruct']
        fetch: true
      titleConvo: true
      titleModel: 'meta-llama/llama-3-70b-instruct'
      dropParams: ['stop']
      modelDisplayLabel: 'OpenRouter'
    - name: 'Ollama'
      apiKey: 'ollama'
      baseURL: 'http://host.docker.internal:11434/v1/'
      models:
        default: ['llama3:latest', 'command-r', 'mixtral', 'phi3']
        fetch: true
      titleConvo: true
      titleModel: 'current_model'
    - name: 'Claude-Compatible'
      provider: 'anthropic'
      apiKey: '${ANTHROPIC_API_KEY}'
      baseURL: 'https://api.anthropic.com'
      headers:
        anthropic-version: '2023-06-01'
      models:
        default: ['claude-sonnet-4-5']
        fetch: false
      titleConvo: true
      titleModel: 'claude-sonnet-4-5'
```

Browse all compatible providers in the [AI Endpoints](/docs/configuration/librechat_yaml/ai_endpoints) section. For the full field reference, see [Custom Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/custom_endpoint).

<Callout type="note" title="Anthropic-Compatible Endpoints">

Use `provider: "anthropic"` only for endpoints that speak the native Anthropic Messages API. For OpenAI-compatible gateways that merely expose Anthropic models, omit `provider` and use the regular OpenAI-compatible custom endpoint shape.

</Callout>

<Callout type="warning" title="API Key Configuration">

When configuring API keys in custom endpoints, you have three options:

1. **Environment variable** (recommended): `apiKey: "${OPENROUTER_KEY}"` -- reads from `.env`
2. **User provided**: `apiKey: "user_provided"` -- users enter their own key in the UI
3. **Direct value** (not recommended): `apiKey: "sk-your-actual-key"` -- stored in plain text

</Callout>

## Step 3. Set Environment Variables

Add the API keys referenced in your `librechat.yaml` to the `.env` file:

```bash filename=".env"
OPENROUTER_KEY=your_openrouter_api_key
```

Each `${VARIABLE_NAME}` in librechat.yaml must have a matching entry in `.env`.

## Step 4. Restart and Verify

After editing configuration files, you must restart LibreChat for changes to take effect.

<Tabs items={['Docker', 'Local']}>
  <Tabs.Tab>

```bash
docker compose down && docker compose up -d
```

  </Tabs.Tab>
  <Tabs.Tab>

Stop the running process (Ctrl+C) and restart:

```bash
npm run backend
```

  </Tabs.Tab>
</Tabs>

Open LibreChat in your browser. Your custom endpoints should appear in the endpoint selector dropdown.

<Callout type="warning" title="Not Seeing Your Endpoint? An Incomplete Block Is Dropped Silently">

Start with the server logs:

```bash
docker compose logs api
```

**But do not stop there.** LibreChat keeps a custom endpoint only if all of `name`, `baseURL`, `apiKey`, and `models` are present, and `models` has either `fetch: true` or a non-empty `default` list. An entry failing that check is removed from the endpoint list with **no error and no log line at all**: the provider simply never appears, and the logs look clean.

So when an endpoint is missing, check the block itself before hunting through logs:

- Is every one of `name`, `baseURL`, `apiKey`, `models` spelled correctly? A single typo drops the whole entry.
- Does `models` have `fetch: true` or at least one entry under `default`?
- Is the block indented **inside** the `endpoints.custom` list, rather than at the top level?
- Is the `name` unique? A second endpoint with the same name (case-insensitively) silently replaces the first.

Compare your block against the [Custom Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/custom_endpoint) reference.

Also note that a schema error **anywhere** in `librechat.yaml` stops the server rather than disabling one section, so one bad block elsewhere can take every custom endpoint down with it. Validate syntax with the [YAML Validator](/docs/toolkit/yaml-validator), which checks YAML syntax only, not LibreChat's schema.

</Callout>

### OpenRouter Still Does Not Show Up

For OpenRouter specifically, verify the three-file chain:

1. `.env` has `OPENROUTER_KEY=...`
2. `librechat.yaml` has `apiKey: "${OPENROUTER_KEY}"` under the OpenRouter custom endpoint
3. Docker users mounted `librechat.yaml` in `docker-compose.override.yml`

Then restart with:

```bash
docker compose down && docker compose up -d
```

If the endpoint appears but returns `402 Payment Required`, the request reached OpenRouter successfully and the issue is usually account credits, billing, or model availability on OpenRouter.

## Next Steps

<Cards num={2}>
  <Cards.Card title="AI Endpoints" href="/docs/configuration/librechat_yaml/ai_endpoints" arrow>
    Browse all compatible AI providers with example configurations
  </Cards.Card>
  <Cards.Card title="librechat.yaml Guide" href="/docs/configuration/librechat_yaml" arrow>
    Full setup guide and reference for the config file
  </Cards.Card>
</Cards>


# Local Installation (https://www.librechat.ai/docs/local)

<LocalInstallHub />

<Callout type="info" title="Looking for a desktop installer?">

LibreChat is a self-hosted web application, not a native Windows app or Linux AppImage. There is no installer to download and run: you start the server using one of the options above, then use LibreChat in your browser.

On Windows, [Docker Desktop](/docs/local/docker) is the simplest route.

</Callout>


# Docker (https://www.librechat.ai/docs/local/docker)

For most scenarios, Docker Compose is the recommended installation method due to its simplicity, ease of use, and reliability.

## Prerequisites

- [`Git`](https://git-scm.com/downloads)
- [`Docker`](https://www.docker.com/products/docker-desktop/)

Docker Desktop is recommended for most users. For remote server installations, see the [Ubuntu Docker Deployment Guide](/docs/remote/docker_linux).

<Callout type="warn" title="Apple Silicon (M-series) Macs">

Mac computers with Apple Silicon (M1, M2, M3, M4) processors do not support AVX instructions, which are required by the default MongoDB image used in LibreChat's Docker Compose setup. If you're on an M-series Mac, MongoDB will crash on startup.

**Fix:** Create a `docker-compose.override.yml` to use an older, compatible MongoDB image:

```yaml filename="docker-compose.override.yml"
services:
  mongodb:
    image: mongo:4.4.18
```

See the [Docker Override guide](/docs/configuration/docker_override) for more details.

</Callout>

## Installation

<Steps>
  <Step>

### Clone the Repository

```bash
git clone https://github.com/danny-avila/LibreChat.git
cd LibreChat
```

  </Step>
  <Step>

### Create Your Environment File

```bash
cp .env.example .env
```

The default `.env` file works out of the box for a basic setup. For in-depth configuration, see the [.env reference](/docs/configuration/dotenv).

<Callout type="info" title="Windows">

On Windows, use `copy .env.example .env` if `cp` is not available.

</Callout>

  </Step>
  <Step>

### Start LibreChat

```bash
docker compose up -d
```

The first launch pulls Docker images and may take a few minutes. Subsequent starts are much faster.

  </Step>
  <Step>

### Verify and Log In

Open your browser and visit [http://localhost:3080](http://localhost:3080). You should see the LibreChat login page.

<Callout type="info" title="First Account = Admin in Single-Tenant Deployments">

In an unscoped single-tenant deployment, the first account you register becomes the admin account. There are no default credentials -- you create your own username and password during registration. Tenant-scoped deployments do not auto-promote the first user in each tenant; provision tenant administrators through a trusted administrative flow.

</Callout>

Click **Register** to create your account and start using LibreChat.

  </Step>
</Steps>

## Mounting librechat.yaml

To use a custom `librechat.yaml` configuration file with Docker, you need to mount it as a volume so the container can access it.

Copy the example override file and edit it:

```bash
cp docker-compose.override.yml.example docker-compose.override.yml
```

Ensure the librechat.yaml volume mount is uncommented in `docker-compose.override.yml`:

```yaml filename="docker-compose.override.yml"
services:
  api:
    volumes:
      - type: bind
        source: ./librechat.yaml
        target: /app/librechat.yaml
```

Restart for changes to take effect:

```bash
docker compose down && docker compose up -d
```

For full setup instructions including creating the file from scratch, see the [librechat.yaml guide](/docs/configuration/librechat_yaml). For more override options, see the [Docker override guide](/docs/configuration/docker_override).

## Updating LibreChat

The following commands will fetch the latest LibreChat project changes, including any necessary changes to the docker compose files, as well as the latest prebuilt images.

<Callout type="info" title="Permissions">

You may need to prefix commands with `sudo` according to your environment permissions.

</Callout>

```bash filename="Stop the running container(s)"
docker compose down
```

```bash filename="Remove all existing docker images"
# Linux/Mac
docker images -a | grep "librechat" | awk '{print $3}' | xargs docker rmi

# Windows (PowerShell)
docker images -a --filter "reference=registry.librechat.ai/danny-avila/librechat*" --format "{{.ID}}" | ForEach-Object { docker rmi $_ }
docker images -a --filter "reference=ghcr.io/danny-avila/librechat*" --format "{{.ID}}" | ForEach-Object { docker rmi $_ }
```

```bash filename="Pull latest project changes"
git pull
```

```bash filename="Pull the latest LibreChat image"
docker compose pull
```

```bash filename="Start LibreChat"
docker compose up
```

## Troubleshooting

### Port Already in Use

If you see an error like `bind: address already in use` for port 3080, another application is using that port.

Either stop the conflicting application, or change the port in `docker-compose.override.yml`:

```yaml filename="docker-compose.override.yml"
services:
  api:
    ports:
      - "3081:3080"
```

Then visit `http://localhost:3081` instead.

### Container Crashes on Startup

If containers exit immediately after starting, check the logs:

```bash
docker compose logs api
```

Common causes:

- Invalid `librechat.yaml` syntax -- validate with the [YAML Validator](/toolkit/yaml_checker)
- Missing `.env` file -- ensure `.env` exists in the project root
- Docker not running -- ensure Docker Desktop is open and running

### Missing Environment Variables

If features are not working as expected, check that required environment variables are set in your `.env` file.

```bash
docker compose exec api env | grep -i "your_variable"
```

See the [.env reference](/docs/configuration/dotenv) for all available variables and their defaults.

## Next Steps

<Cards num={3}>
  <Cards.Card title="Custom Endpoints" href="/docs/quick_start/custom_endpoints" arrow>
    Add OpenRouter, Ollama, and other AI providers
  </Cards.Card>
  <Cards.Card title="Configuration Overview" href="/docs/configuration" arrow>
    Understand how LibreChat's config files work together
  </Cards.Card>
  <Cards.Card title="Authentication Setup" href="/docs/configuration/authentication" arrow>
    Configure OAuth, LDAP, and other login methods
  </Cards.Card>
</Cards>


# npm (https://www.librechat.ai/docs/local/npm)

For most scenarios, Docker Compose is the recommended installation method due to its simplicity, ease of use, and reliability. If you prefer using npm, you can follow these instructions.

## Prerequisites

- Node.js `v24.16.0`: [https://nodejs.org/en/download](https://nodejs.org/en/download)
- npm `v11.16.0`
  - LibreChat uses CommonJS (CJS) and openid-client v6; Node 24 satisfies the required CJS/ESM
    interop, WebCrypto, and Fetch API runtime support.
- Git: https://git-scm.com/download/
- MongoDB (Atlas or Community Server)
  - [MongoDB Atlas](/docs/configuration/mongodb/mongodb_atlas)
  - [MongoDB Community Server](/docs/configuration/mongodb/mongodb_community)

If you use `nvm`, install and select the recommended Node.js version, then update npm:

```bash filename="Use Node.js 24 and npm 11"
nvm install 24.16.0
nvm use 24.16.0
npm install -g npm@11.16.0
node -v
npm -v
```

You should see `v24.16.0` for Node.js and `11.16.0` for npm before installing LibreChat
dependencies.

## Installation Steps

### Preparation

Run the following commands in your terminal:

```bash filename="Clone the Repository"
git clone https://github.com/danny-avila/LibreChat.git
```

```bash filename="Navigate to the LibreChat Directory"
cd LibreChat
```

```bash filename="Create a .env File from .env.example"
cp .env.example .env
```

> **Note:** **If you're using Windows 10, you might need to use `copy` instead of `cp`.**

```bash filename="Update the MONGO_URI"
Important: Edit the newly created `.env` file to update the `MONGO_URI` with your own MongoDB instance URI.
```

<Callout title="Update the MONGO_URI" emoji="❗">
  Edit the newly created `.env` file to update the `MONGO_URI` with your own
</Callout>

### Build and Start

Once you've completed the preparation steps, run the following commands:

```bash filename="Install dependencies"
npm run reinstall
```

`npm run reinstall` performs a clean dependency install and builds LibreChat. Use it after changing
Node.js or npm versions so native packages are rebuilt against the active runtime.

```bash filename="Start LibreChat!"
npm run backend
```

<Callout type="success" title="Access LibreChat!" emoji="🎉">
  **Visit [http://localhost:3080/](http://localhost:3080/)**
</Callout>

<Callout type="example" title="Tip" emoji="🔥">
  - Next time you want to start LibreChat, you only need to execute `npm run backend`
</Callout>

## Update LibreChat

To update LibreChat to the latest version, run the following commands:

<Callout type="warning" emoji="">
  First, stop LibreChat (if you haven't already).
</Callout>

```bash filename="Pull latest project changes"
git pull
```

```bash filename="Update dependencies"
npm run smart-reinstall
```

If you changed Node.js or npm versions during the update, run `npm run reinstall` instead.

```bash filename="Start LibreChat!"
npm run backend
```

## Additional Setup

Unlock additional features by exploring our configuration guides to learn how to set up:

- Meilisearch integration
- RAG API connectivity
- Custom endpoints
- Other advanced configuration options
- And more

This will enable you to customize your LibreChat experience with optional features.

**see also:**
- [User Authentication System Setup](/docs/configuration/authentication)
- [AI Setup](/docs/configuration/pre_configured_ai)
- [Custom Endpoints & Configuration](/docs/configuration/librechat_yaml)


# Helm Chart (https://www.librechat.ai/docs/local/helm_chart)

Please follow this guidance to deploy LibreChat on Kubernetes using Helm, adjusting as needed for your specific use case. Other Helm charts contributed by the community are listed below in the [Community Helm Charts](#community-helm-charts) section.

## Prerequisites

* A running Kubernetes cluster
* _Local_ installations of `kubectl` and Helm

## Configuration

1. Use the [Credentials Generator](/toolkit/creds_generator) to generate secure values for `CREDS_KEY`, `CREDS_IV`, `JWT_SECRET`, `JWT_REFRESH_SECRET` and `MEILI_MASTER_KEY`.
Place them in a Kubernetes Secret like this (if you change the secret name, remember to update your Helm values):

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: librechat-credentials-env
  namespace: <librechat-chart-namespace>
type: Opaque
stringData:
  CREDS_KEY: <generated value>
  CREDS_IV: <generated value>
  JWT_SECRET: <generated value>
  JWT_REFRESH_SECRET: <generated value>
  MEILI_MASTER_KEY: <generated value>
```

Use permanent values and mount the same Secret into every replica. Do not rely on process-local or ephemeral filesystem credentials in Kubernetes: changing these values can invalidate sessions and make existing encrypted records unreadable.
2. Add to this same secret any required API keys for LLM providers:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: librechat-credentials-env
  namespace: <librechat-chart-namespace>
. . . .

  OPENAI_API_KEY: <your secret value>
```
3. Apply the Secret to the Cluster:

## Install Helm Chart

To install the helm chart run: 

`helm install <deployment-name> oci://ghcr.io/danny-avila/librechat-chart/librechat`

<details>
  <summary>Development version</summary>

In the repo's root directory, run: 

`helm install <deployment-name> ./helm/librechat`
</details>

Similar to other Helm charts, there exists a [values file](https://github.com/danny-avila/LibreChat/blob/main/helm/librechat/values.yaml) that outlines the default settings and indicates which configuration options can be modified.

Create a `values.yaml` file populated with the values you want to modify from the default.

Install the Helm chart: `helm install librechat oci://ghcr.io/danny-avila/librechat-chart/librechat --values <values-override-filel>`

## Uninstall the Helm Chart

To uninstall the Helm Chart: `helm uninstall <deployment-name>`

Example: `helm uninstall librechat`

## Migrate 1.x -> 2.x
If you used the chart before version 2.x you may need to update the `value` structure.

1. Move Config to librechat.configEnv:
```diff
- env:
-     ALLOW_EMAIL_LOGIN: "true"
-     ALLOW_REGISTRATION: "true"
+ librechat:
+   configEnv:
+     ALLOW_REGISTRATION: "true"
+     ALLOW_EMAIL_LOGIN: "true"
```

2. Consolidate all Secret values to a single Secret as described in [Configuration Step 1](#configuration). 
3. To leverage an external MongoDB instance, refer to the [values file](https://github.com/danny-avila/LibreChat/blob/main/helm/librechat/values.yaml) of the Chart, deactivate the components accordingly and change the FQDN of the Mongodb instance. This is recommended if data already exists in this externally managed MongoDB instance.
  
## Community Helm Charts
- [Blue Atlas Helm Charts](https://charts.blue-atlas.de/) # deprecated now that LibreChat provides an official chart
- Submitted by [@dimaby](https://github.com/dimaby) on GitHub: [PR #2879](https://github.com/danny-avila/LibreChat/pull/2879)


# Overview (https://www.librechat.ai/docs/remote)

Welcome to the introductory guide for deploying LibreChat. This document provides an initial overview, featuring a comparison table and references to detailed guides, ensuring a thorough understanding of deployment strategies.

In this guide, you will explore various options to efficiently deploy LibreChat in a variety of environments, customized to meet your specific requirements.

## Minimum Requirements

The minimum requirements for running LibreChat:

- 1 GiB RAM
- 1 vCPU

**Note:** With everything enabled, you might consider increasing the RAM to 2GB for smoother operation.

## Comparative Table

> Note that the "Recommended" label indicates that these services are well-documented, widely used within the community, or have been successfully deployed by a significant number of users. As a result, we're able to offer better support for deploying LibreChat on these services

### Hosting Services

| **Service**                                | **Domain**                | **Pros**                                                   | **Cons**                               | **Comments**                                            |      **Recommended**    |
|--------------------------------------------|---------------------------|------------------------------------------------------------|----------------------------------------|---------------------------------------------------------|-------------------------|
| [DigitalOcean](/docs/remote/digitalocean)  | Cloud Infrastructure      | Intuitive interface, stable pricing                        | Smaller network footprint              | Optimal for enthusiasts & small to medium businesses    | ✅ Well Known, Reliable |
| [HuggingFace](/docs/remote/huggingface)    | AI/ML Solutions           | ML/NLP specialization                                      | Focused on ML applications             | Excellent for AI/ML initiatives                         | ✅ Free                 |
| [Railway](/docs/remote/railway)            | App Deployment            | Simplified app deployment                                  | Limited access to containers           | Very easy to get started                                | ✅ Easy                 |

### Network Services 

| **Service**                           | **Domain**                  | **Pros**                                            | **Cons**                                         | **Comments**                                    |
|---------------------------------------|-----------------------------|-----------------------------------------------------|--------------------------------------------------|-------------------------------------------------|
| [Cloudflare](/docs/remote/cloudflare) | Web Performance & Security  | Global CDN, DDoS protection, ease of use            | Customer support can be slow                     | Top choice for security enhancements            |
| [Nginx](/docs/remote/nginx)           | Web Server, Reverse Proxy   | High performance, stability, resource efficiency    | Manual setup, limited extensions                 | Widely used for hosting due to its performance  |
| [ngrok](/docs/remote/ngrok)           | Secure Tunneling            | Easy to use, free tier available, secure tunneling  | Requires client download, complex domain routing | Handy for local development tests               |
| [Traefik](/docs/remote/traefik)       | Reverse Proxy, Load Balancer| Automatic service discovery, native cluster support | Configuration can be complex for beginners       | Ideal for microservices and dynamic environments|


**Cloudflare** is known for its extensive network that speeds up and secures internet services, with an intuitive user interface and robust security options on premium plans.

**Ngrok** is praised for its simplicity and the ability to quickly expose local servers to the internet, making it ideal for demos and testing.

**Nginx** is a high-performance web server that is efficient in handling resources and offers stability. It does, however, require manual setup and has fewer modules and extensions compared to other servers.

**Traefik**  is renowned for its automatic configuration updates and ease of deployment in container environments, appealing to DevOps for its integration with various back-ends and dynamic reconfiguration. It thrives in microservices architectures but may pose challenges for those new to cloud-native technologies.

## Cloud Vendor Integration and Configuration

The integration level with cloud vendors varies: from platforms enabling single-click LibreChat deployments like [Railway](/docs/remote/railway), through platforms leveraging Infrastructure as Code tools such as Azure with Terraform, to more traditional VM setups requiring manual configuration, exemplified by [DigitalOcean](/docs/remote/digitalocean), Linode, and Hetzner.

## Essential Security Considerations

Venturing into the digital landscape reveals numerous threats to the security and integrity of your online assets. To safeguard your digital domain, it is crucial to implement robust security measures.

When deploying applications on a global scale, it is essential to consider the following key factors to ensure the protection of your digital assets:

1. Encrypting data in transit: Implementing HTTPS with SSL certificates is vital to protect your data from interception and eavesdropping attacks.
2. Global accessibility implications: Understand the implications of deploying your application globally, including the legal and compliance requirements that vary by region.
3. Secure configuration: Ensure that your application is configured securely, including the use of secure protocols, secure authentication, and authorization mechanisms.

If you choose to use IaaS or Tunnel services for your deployment, you may need to utilize a reverse proxy such as [Nginx](/docs/remote/nginx), [Traefik](/docs/remote/traefik) or [Cloudflare](/docs/remote/cloudflare) to name a few.

Investing in the appropriate security measures is crucial to safeguarding your digital assets and ensuring the success of your global deployment.

## Choosing the Cloud vendor (e.g. platform)

Choosing a cloud vendor, for the "real deployment" is crucial as it impacts cost, performance, security, and scalability. You should consider factors such as data center locations, compliance with industry standards, compatibility with existing tools, and customer support.

There is a lot of options that differ in many aspects. In this section you can find some options that the team and the community uses that can help you in your first deployment.
Once you gain more knowledge on your application usage and audience you will probably be in a position to decide what cloud vendor fits you the best for the long run.

As said the cloud providers / platforms differ in many aspects. For our purpose we can assume that in our context your main concerns is will ease of use, security and (initial) cost. In case that you have more concerns like scaling, previous experience with any of the platforms or any other specific feature then you probably know better what platform fit's you and you can jump directly to the information that you are seeking without following any specific guide.

## Choosing the Right Deployment Option for Your Needs

The deployment options are listed in order from most effort and control to least effort and control

> Each deployment option has its advantages and disadvantages, and the choice ultimately depends on the specific needs of your project.

### 1. IaaS (Infrastructure as a Service)

Infrastructure as a Service (IaaS) refers to a model of cloud computing that provides fundamental computing resources, such as virtual servers, network, and storage, on a pay-per-use basis. IaaS allows organizations to rent and access these resources over the internet, without the need for investing in and maintaining physical hardware. This model provides scalability, flexibility, and cost savings, as well as the ability to quickly and easily deploy and manage infrastructure resources in response to changing business needs.

- [DigitalOcean](/docs/remote/digitalocean): User-friendly interface with predictable pricing.
- Linode: Renowned for excellent customer support and straightforward pricing.

#### For Iaas we recommend Docker Compose

**Why Docker Compose?** We recommend Docker Compose for consistent deployments. This guide clearly outlines each step for easy deployment: [Docker - Linux remote install guide](/docs/remote/docker_linux)

**Note:** There are two docker compose files in the repo

1. **Development Oriented docker compose `docker-compose.yml`**
2. **Deployment Oriented docker compose `deploy-compose.yml`**

The main difference is that `deploy-compose.yml` includes Nginx, making its configuration internal to Docker.

> Look at the [Nginx Guide](/docs/remote/nginx) for more information

### 2. IaC (Infrastructure as Code)

Infrastructure as Code (IaC) refers to the practice of managing and provisioning computing infrastructures through machine-readable definition files, as opposed to physical hardware configuration or interactive configuration tools. This approach promotes reproducibility, disposability, and scalability, particularly in modern cloud environments. IaC allows for the automation of infrastructure deployment, configuration, and management, resulting in faster, more consistent, and more reliable provisioning of resources.

- Azure: Comprehensive services suitable for enterprise-level deployments

**Note:** Digital Ocean, Linode, Hetzner also support IaC. While we lack a specific guide, you can try to adapt the adapt the Azure Guide for Terraform and help us contribute to its enhancement.

### 3. PaaS (Platform as a Service)

Platform as a Service (PaaS) is a model of cloud computing that offers a development and deployment environment in the cloud. It provides a platform for developers to build, test, and deploy applications, without the need for managing the underlying infrastructure. PaaS typically includes a range of resources such as databases, middleware, and development tools, enabling users to deliver simple cloud-based apps to sophisticated enterprise applications. This model allows for faster time-to-market, lower costs, and easier maintenance and scaling, as the service provider is responsible for maintaining the infrastructure, and the customer can focus on building, deploying and managing their applications.

- [Hugging Face](/docs/remote/huggingface): Tailored for machine learning and NLP projects.
- Render: Simplifies deployments with integrated CI/CD pipelines.
- Heroku: Optimal for startups and quick deployment scenarios.

### 4. One Click Deployment (PaaS)

- [Railway](/docs/remote/railway): Popular one-click deployment solution
- Zeabur: Pioneering effortless one-click deployment solutions.

## Other / Network Services

### 1. Tunneling

Tunneling services allow you to expose a local development server to the internet, making it accessible via a public URL. This is particularly useful for sharing work, testing, and integrating with third-party services. It allows you to deploy your development computer for testing or for on-prem installation.

- [Ngrok](/docs/remote/ngrok): Facilitates secure local tunneling to the internet.
- [Cloudflare](/docs/remote/cloudflare): Enhances web performance and security.

### 2. DNS Service

- Cloudflare DNS service is used to manage and route internet traffic to the correct destinations, by translating human-readable domain names into machine-readable IP addresses. Cloudflare is a provider of this service, offering a wide range of features such as security, performance, and reliability. The Cloudflare DNS service provides a user-friendly interface for managing DNS records, and offers advanced features such as traffic management, DNSSEC, and DDoS protection.

see also: [Cloudflare Guide](/docs/remote/cloudflare)

## Conclusion

In conclusion, the introduction of our deployment guide provides an overview of the various options and considerations for deploying LibreChat. It is important to carefully evaluate your needs and choose the path that best aligns with your organization's goals and objectives. Whether you prioritize ease of use, security, or affordability, our guide provides the necessary information to help you successfully deploy LibreChat and achieve your desired outcome. We hope that this guide will serve as a valuable resource for you throughout your deployment journey.

Remember, our community is here to assist. Should you encounter challenges or have queries, our [Discord channel](https://discord.librechat.ai) and [troubleshooting discussion](https://github.com/danny-avila/LibreChat/discussions/categories/troubleshooting) are excellent resources for support and advice.


# DigitalOcean (https://www.librechat.ai/docs/remote/digitalocean)

> These instructions + the [docker guide](/docs/remote/docker_linux) are designed for someone starting from scratch for a Docker Installation on a remote Ubuntu server. You can skip to any point that is useful for you. There are probably more efficient/scalable ways, but this guide works really great for my personal use case.

**There are many ways to go about this, but I will present to you the best and easiest methods I'm aware of. These configurations can vary based on your liking or needs.**

Digital Ocean is a great option for deployment: you can benefit off a **free [200 USD credit](https://www.digitalocean.com/?refcode=4486923fcf00&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)** (for 60 days), and one of the cheapest tiers (6 USD/mo) will work for LibreChat in a low-stress, minimal-user environment. Should your resource needs increase, you can always upgrade very easily.

Digital Ocean is also my preferred choice for testing deployment, as it comes with useful resource monitoring and server access tools right out of the box.

**Using the following Digital Ocean link will directly support the project by helping me cover deployment costs with credits!**

## **Click the banner to get a $200 credit and to directly support LibreChat!**

_You are free to use this credit as you wish!_

[![DigitalOcean Referral Badge](https://web-platforms.sfo2.cdn.digitaloceanspaces.com/WWW/Badge%201.svg)](https://www.digitalocean.com/?refcode=4486923fcf00&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)

_Note: you will need a credit card or PayPal to sign up. I'm able to use a prepaid debit card through PayPal for my billing_

## Table of Contents

- **[Part I: Starting from Zero](#part-i-starting-from-zero)**
  - [1. DigitalOcean signup](#1-get-started-on-digitalocean)
  - [2. Access console](#2-access-your-droplet-console)
  - [3. Console user setup](#3-once-you-have-logged-in-immediately-create-a-new-non-root-user)
  - [4. Firewall Setup](#4-firewall-setup)
- **[Part II: Installing Docker & Other Dependencies](/docs/remote/docker_linux)**

## Part I: Starting from Zero:

### **1. Get started on DigitalOcean**

[Click here](https://www.digitalocean.com/?refcode=4486923fcf00&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge) or on the banner above to get started.

Once you're logged in, you will be greeted with a [nice welcome screen](https://cloud.digitalocean.com/welcome).

![image](https://github.com/danny-avila/LibreChat/assets/110412045/b7a71eae-770e-4c69-a5d4-d21b939d64ed)

### **a) Navigate to the Projects page**

Click on ["Explore our control panel"](https://cloud.digitalocean.com/projects) or simply navigate to the [Projects page](https://cloud.digitalocean.com/projects).

Server instances are called **"droplets"** in digitalocean, and they are organized under **"Projects."**

### **b) Click on "Spin up a Droplet" to start the setup**

![image](https://github.com/danny-avila/LibreChat/assets/110412045/6046e8cd-ff59-4795-a29a-5f44ab2f0a6d)

Adjust these settings based on your needs, as I'm selecting the bare minimum/cheapest options that will work.

- **Choose Region/Datacenter:** closest to you and your users
- **Choose an image:** Ubuntu 22.04 (LTS) x64
- **Choose Size:** Shared CPU, Basic Plan
  - CPU options: Regular, 6 USD/mo option (0.009 USD/hour, 1 GB RAM / 1 CPU / 25 GB SSD / 1000 GB transfer)
  - No additional storage
- **Choose Authentication Method:** Password option is easiest but up to you
  - Alternatively, you can setup traditional SSH.
- **Recommended:** Add improved metrics monitoring and alerting (free)
  - You might be able to get away with the $4/mo option by not selecting this, but not yet tested
- **Finalize Details:**
  - Change the hostname to whatever you like, everything else I leave default (1 droplet, no tags)
  - Finally, click "Create Droplet"

![image](https://github.com/danny-avila/LibreChat/assets/110412045/ac90d40e-3ac6-482f-885c-58058c5e3f76)

After creating the droplet, it will now spin up with a progress bar.

### **2. Access your droplet console**

Once it's spun up, **click on the droplet** and click on the Console link on the right-hand side to start up the console.

![image](https://github.com/danny-avila/LibreChat/assets/110412045/47c14280-fe48-49b9-9997-ff4d9c83212c)

![image](https://github.com/danny-avila/LibreChat/assets/110412045/d5e518fd-4941-4b35-86cc-69f8f65ec8eb)

Launching the Droplet console this way is the easiest method but you can also SSH if you set it up in the previous step.

To keep this guide simple, I will keep it easy and continue with the droplet console. Here is an [official DigitalOcean guide for SSH](https://docs.digitalocean.com/products/droplets/how-to/connect-with-ssh/) if you are interested.

### **3. Once you have logged in, immediately create a new, non-root user:**

**Note:** you should remove the greater/less than signs anytime you see them in this guide

```bash
# example: adduser danny
adduser <yourusername>
# you will then be prompted for a password and user details
```

Once you are done, run the following command to elevate the user

```bash
# example: usermod -aG sudo danny
usermod -aG sudo <yourusername>
```

**Make sure you have done this correctly by double-checking you have sudo permissions:**

```bash
getent group sudo | cut -d: -f4
```

**Switch to the new user**

```bash
# example: su - danny
su - <yourusername>
```

### **4. Firewall Setup**

It's highly recommended you setup a simple firewall for your setup.

Click on your droplet from the projects page again, and goto the Networking tab on the left-hand side under your ipv4:

![image](https://github.com/danny-avila/LibreChat/assets/110412045/20a2f31b-83ec-4052-bca7-27a672c3770a)

Create a firewall, add your droplet to it, and add these inbound rules (will work for this guide, but configure as needed)

![image](https://github.com/danny-avila/LibreChat/assets/110412045/d9bbdd7b-3702-4d2d-899b-c6457e6d221a)

---

This concludes the initial setup. For the subsequent steps, please proceed to the next guide:**[Docker Deployment Guide](/docs/remote/docker_linux)**, which will walk you through the remaining installation process.


# Docker (Remote Linux) (https://www.librechat.ai/docs/remote/docker_linux)

In order to use this guide you need a remote computer or VM deployed. While you can use this guide with a local installation, keep in mind that it was originally written for cloud deployment.

> ⚠️ This guide was originally designed for [Digital Ocean](/docs/remote/digitalocean), so you may have to modify the instruction for other platforms, but the main idea remains unchanged.

## Part I: Installing Docker and Other Dependencies:

There are many ways to setup Docker on Linux systems. I'll walk you through the best and the recommended way [based on this guide](https://www.smarthomebeginner.com/install-docker-on-ubuntu-22-04/).

> Note that the "Best" way for Ubuntu docker installation does not mean the "fastest" or the "easiest". It means, the best way to install it for long-term benefit (i.e. faster updates, security patches, etc.).

### **1. Update and Install Docker Dependencies**

First, let's update our packages list and install the required docker dependencies.

```bash
sudo apt update
```

Then, use the following command to install the dependencies or pre-requisite packages.

```bash
sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
```

#### **Installation Notes**

- Input "Y" for all [Y/n] (yes/no) terminal prompts throughout this entire guide.
- After the first [Y/n] prompt, you will get the first of a few **purple screens** asking to restart services.
  - Each time this happens, you can safely press ENTER for the default, already selected options:

![image](https://github.com/danny-avila/LibreChat/assets/110412045/05cf165b-d3d8-475a-93b3-254f3c63f59b)

- If at any point your droplet console disconnects, do the following and then pick up where you left off:
  - Access the console again as indicated above
  - Switch to the user you created with `su - <yourusername>`

### **2. Add Docker Repository to APT Sources**

While installing Docker Engine from Ubuntu repositories is easier, adding official docker repository gives you faster updates. Hence why this is the recommended method.

First, let us get the GPG key which is needed to connect to the Docker repository. To that, use the following command.

```bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```

Next, add the repository to the sources list. While you can also add it manually, the command below will do it automatically for you.

```bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

The above command will automatically fill in your release code name (jammy for 22.04, focal for 20.04, and bionic for 18.04).

Finally, refresh your packages again.

```bash
sudo apt update
```

If you forget to add the GPG key, then the above step would fail with an error message. Otherwise, let's get on with installing Docker on Ubuntu.

### **3. Install Docker**

> What is the difference between docker.io and docker-ce?

> docker.io is the docker package that is offered by some popular Linux distributions (e.g. Ubuntu/Debian). docker-ce on the other hand, is the docker package from official Docker repository. Typically docker-ce more up-to-date and preferred.

We will now install the docker-ce (and not docker.io package)

```bash
sudo apt install docker-ce
```

Purple screen means press ENTER. :)

Recommended: you should make sure the created user is added to the docker group for seamless use of commands:

```bash
sudo usermod -aG docker $USER
```

Now let's reboot the system to make sure all is well.

```bash
sudo reboot
```

After rebooting, if using the browser droplet console, you can click reload and wait to get back into the console.

![image](https://github.com/danny-avila/LibreChat/assets/110412045/2ad7b739-a3db-4744-813f-39af7dabfce7)

**Reminder:** Any time you reboot with `sudo reboot`, you should switch to the user you setup as before with `su - <yourusername>`.

### **4. Verify that Docker is Running on Ubuntu**

There are many ways to check if Docker is running on Ubuntu. One way is to use the following command:

```bash
sudo systemctl status docker
```

You should see an output that says **active (running)** for status.

![image](https://github.com/danny-avila/LibreChat/assets/110412045/6baea405-8dfb-4d9d-9327-6e9ecf800471)

Exit this log by pressing CTRL (or CMD) + C.

### **5. Install Docker Compose**

Since we already added Docker's official repository in step 2, installing Docker Compose is straightforward using the [official Compose plugin](https://docs.docker.com/compose/install/linux/):

```bash
sudo apt install docker-compose-plugin
```

Verify the installation:

```bash
docker compose version
```

> Note: Docker Compose v2 uses the `docker compose` command (without hyphen) instead of the legacy `docker-compose`. All commands in this guide use the modern syntax.

### **6. As part of this guide, I will recommend you have git and npm installed:**

Though not technically required, having git and npm will make installing/updating very simple:

```bash
sudo apt install git nodejs npm
```

Cue the matrix lines.

You can confirm these packages installed successfully with the following:

```bash
git --version
node -v
npm -v
```

![image](https://github.com/danny-avila/LibreChat/assets/110412045/fbba1a38-95cd-4e8e-b813-04001bb82b25)

> Note: this may install old Node.js and npm versions. If you run LibreChat directly on the host, use Node.js `v24.16.0` with npm `v11.16.0`. For this Docker-based guide, the host Node.js version does not matter because LibreChat runs inside containers.

**Ok, now that you have set up the Droplet, you will now setup the app itself**

---

## Part II: Setup LibreChat

### **1. Clone down the repo**

From the _droplet_ commandline (as your user, not root):

```bash
# clone down the repository
git clone https://github.com/danny-avila/LibreChat.git

# enter the project directory
cd LibreChat/
```

### **2. Create LibreChat Config and Environment files**

#### Config (librechat.yaml) File

Next, we create the [LibreChat Config file](/docs/configuration/librechat_yaml), AKA `librechat.yaml`, allowing for customization of the app's settings as well as [custom endpoints](/docs/configuration/librechat_yaml/ai_endpoints).

Whether or not you want to customize the app further, it's required for the `deploy-compose.yml` file we are using, so we can create one with the bare-minimum value to start:

```bash
nano librechat.yaml
```

You will enter the editor screen, and you can paste the following:

```yaml
# For more information, see the Configuration Guide:
# https://www.librechat.ai/docs/configuration/librechat_yaml

# Configuration version (required)
version: 1.3.5
# This setting caches the config file for faster loading across app lifecycle
cache: true
```

Exit the editor with `CTRL + X`, then `Y` to save, and `ENTER` to confirm.

<Callout type="info" title="Configuration Validation">
LibreChat will exit with an error (exit code 1) if your `librechat.yaml` file contains validation errors. This fail-fast behavior ensures configuration issues are caught early in deployment.

Before deploying, validate your configuration using the [YAML Validator](/toolkit/yaml_checker). If your CI/CD pipeline starts the server, it will fail fast on invalid configuration, preventing deployments with misconfigured settings.
</Callout>

#### Environment (.env) File

The example file is enough to start a single Docker Compose deployment and provide model credentials from the web app.

```bash
# Copies the example file as your global env file
cp .env.example .env
```

`CREDS_KEY`, `CREDS_IV`, `JWT_SECRET`, and `JWT_REFRESH_SECRET` are blank by default. On first startup, LibreChat generates unique temporary values and stores them at `/app/data/.env.temp`; the bundled `librechat-data` volume keeps that file across container recreation.

For production, generate permanent values instead:

[Credentials Generator](/docs/toolkit/credentials-generator)

```bash
nano .env

# Set unique, permanent values before production use.

# Must be a 16-byte IV (32 characters in hex)

CREDS_IV=<your-generated-value>

# Must be 32-byte keys (64 characters in hex)

CREDS_KEY=<your-generated-value>
JWT_SECRET=<your-generated-value>
JWT_REFRESH_SECRET=<your-generated-value>
```

Keep these values and the `librechat-data` volume private. Every replica must use the same permanent values. Losing the temporary file or changing established credentials can invalidate sessions and make encrypted records unreadable; LibreChat warns when active credential fingerprints no longer match the database marker.

If you'd like to provide any credentials for all users of your instance to consume, you should add them while you're still editing this file:

```bash
OPENAI_API_KEY=sk-yourKey
```

As before, exit the editor with `CTRL + X`, then `Y` to save, and `ENTER` to confirm.

**That's it!**

For thorough configuration, however, you should edit your .env file as needed, and do read the comments in the file and the resources below.

```bash
# if editing the .env file
nano .env
```

This is one such env variable to be mindful of. This disables external signups, in case you would like to set it after you've created your account.

```shell
ALLOW_REGISTRATION=false
```

**Resources:**
- [Tokens/Apis/etc](/docs/configuration/pre_configured_ai)
- [User/Auth System](/docs/configuration/authentication)

### **3. Start docker**

```bash
# should already be running, but just to be safe
sudo systemctl start docker

# confirm docker is running
docker info
```

Now we can start the app container. For the first time, we'll use the full command and later we can use a shorthand command

```bash
sudo docker compose -f ./deploy-compose.yml up -d
```

![image](https://github.com/danny-avila/LibreChat/assets/110412045/5e2f6627-8ca4-4fa3-be73-481539532ee7)

It's safe to close the terminal if you wish -- the docker app will continue to run.

> Note: this is using a special compose file optimized for this deployed environment. If you would like more configuration here, you should inspect the deploy-compose.yml and Dockerfile.multi files to see how they are setup. We are not building the image in this environment since it's not enough RAM to properly do so. Instead, we pull the latest dev-api image of librechat, which is automatically built after each push to main.

> If you are setting up a domain to be used with LibreChat, this compose file is using the nginx file located in client/nginx.conf. Instructions on this below in part V.

### **4. Once the app is running, you can access it at `http://yourserverip`**

#### Go back to the droplet page to get your server ip, copy it, and paste it into your browser!

![image](https://github.com/danny-avila/LibreChat/assets/110412045/d8bbad29-6015-46ec-88ce-a72a43d8a313)

#### Sign up, log in, and enjoy your own privately hosted, remote LibreChat :)

![image](https://github.com/danny-avila/LibreChat/assets/110412045/85070a54-eb57-479f-8011-f63c14116ee3)

![image](https://github.com/danny-avila/LibreChat/assets/110412045/b3fc2152-4b6f-46f9-81e7-4200b76bc468)

## Part III: Updating LibreChat

I've made this step pretty painless, provided everything above was installed successfully and you haven't edited the git history.

> Note: If you are working on an edited branch, with your own commits, for example, such as with edits to client/nginx.conf, you should inspect config/deployed-update.js to run some of the commands manually as you see fit. See part V for more on this.

Run the following for an automated update

```bash
npm run update:deployed
```

After pulling the current images, the update script runs `sudo docker image prune -f` to remove unused dangling images left by prior versions. This can reclaim substantial disk space, including dangling images unrelated to LibreChat; tagged images, containers, and volumes are not removed.

**Stopping the docker container**

```bash
npm run stop:deployed
```

> This simply runs `docker compose -f ./deploy-compose.yml down`

**Starting the docker container**

```bash
npm run start:deployed
```

> This simply runs `docker compose -f ./deploy-compose.yml up -d`

**Check active docker containers**

```bash
docker ps
```

You can update manually without the scripts if you encounter issues.

```bash filename="Stop the running container(s)""
docker compose -f ./deploy-compose.yml down
```

```bash filename="Remove all existing docker images"
# Linux/Mac
docker images -a | grep "librechat" | awk '{print $3}' | xargs docker rmi

# Windows (PowerShell)
docker images -a --filter "reference=registry.librechat.ai/danny-avila/librechat*" --format "{{.ID}}" | ForEach-Object { docker rmi $_ }
docker images -a --filter "reference=ghcr.io/danny-avila/librechat*" --format "{{.ID}}" | ForEach-Object { docker rmi $_ }
```

```bash filename="Pull latest project changes"
git pull
```

```bash filename="Pull the latest LibreChat image""
docker compose -f ./deploy-compose.yml pull
```

```bash filename="Start LibreChat"
docker compose -f ./deploy-compose.yml up
```

## Part IV: Editing the NGINX file (for custom domains and advanced configs)

In case you would like to edit the NGINX file for whatever reason, such as pointing your server to a custom domain, use the following:

```bash filename="First, stop the active instance if running"
npm run stop:deployed
```
```bash filename="now you can safely edit"
nano client/nginx.conf
```

I won't be walking you through custom domain setup or any other changes to NGINX, you can look into the [Cloudflare guide](/docs/remote/cloudflare), the [Traefik guide](/docs/remote/traefik) or the [NGINX guide](/docs/remote/nginx) to get you started with custom domains.

However, I will show you what to edit on the LibreChat side for a custom domain with this setup.

Since NGINX is being used as a proxy pass by default, I only edit the following:

```shell
# before
server_name localhost;

# after
server_name custom.domain.com;
```

> Note: this works because the deploy-compose.yml file is using NGINX by default, unlike the main docker-compose.yml file. As always, you can configure the compose files as you need.

Now commit these changes to a separate branch:

```bash
# create a new branch
# example: git checkout -b edit
git checkout -b <branchname>

# stage all file changes
git add .
```

To commit changes to a git branch, you will need to identify yourself on git. These can be fake values, but if you would like them to sync up with GitHub, should you push this branch to a forked repo of LibreChat, use your GitHub email

```bash
# these values will work if you don't care what they are
git config --global user.email "you@example.com"
git config --global user.name "Your Name"

# Now you can commit the change
git commit -m "edited nginx.conf"
```

Updating on an edited branch will work a little differently now

```bash
npm run rebase:deployed
```

You should be all set!

> **Warning** You will experience merge conflicts if you start significantly editing the branch and this is not recommended unless you know what you're doing

> Note that any changes to the code in this environment won't be reflected because the compose file is pulling the docker images built automatically by GitHub

## Part V: Use the Latest Stable Release instead of Latest Main Branch

By default, this setup will pull the latest updates to the main branch of Librechat. If you would rather have the latest "stable" release, which is defined by the [latest tags](https://github.com/danny-avila/LibreChat/releases), you will need to edit deploy-compose.yml and commit your changes exactly as above in Part V. Be aware that you won't benefit from the latest feature as soon as they come if you do so.

Let's edit `deploy-compose.yml`:

```bash
nano deploy-compose.yml
```

Change `librechat-dev-api` to `librechat-api`:

```yaml
image: registry.librechat.ai/danny-avila/librechat-api:latest
```

Stage and commit as in Part V, and you're all set!


# HuggingFace (https://www.librechat.ai/docs/remote/huggingface)

## Create and Configure your Database (Required)

The first thing you need is to create a MongoDB Atlas Database and get your connection string.

Follow the instructions in this document: **[MongoDB Atlas](/docs/configuration/mongodb/mongodb_atlas)**

## Getting Started

**1.** Login or Create an account on **[Hugging Face](https://huggingface.co/)**

**2.** Visit **[https://huggingface.co/spaces/LibreChat/template](https://huggingface.co/spaces/LibreChat/template)** and click on `Duplicate this Space` to copy the LibreChat template into your profile. 

> Note: It is normal for this template to have a runtime error, you will have to configure it using the following guide to make it functional.

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/fd684254-cbe0-4039-ba4a-7c492b16a453)

**3.** Name your Space and Fill the `Secrets` and `Variables`
 
  >You can also decide here to make it public or private

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/13a039b9-bb78-4d56-bab1-74eb48171516)

You will need to fill these values:

| Secrets | Values |
| --- | --- |
| MONGO_URI | * use these instruction to get the string: https://librechat.ai/docs/configuration/mongodb/mongodb_atlas |
| OPENAI_API_KEY | `user_provided` | 
| BINGAI_TOKEN | `user_provided` | 
| CHATGPT_TOKEN | `user_provided` |
| ANTHROPIC_API_KEY | `user_provided` |
| GOOGLE_KEY | `user_provided` |
| CREDS_KEY | * see below |
| CREDS_IV | * see below |
| JWT_SECRET | * see below |
| JWT_REFRESH_SECRET | * see below |

> ⬆️ **Leave the value field blank for any endpoints that you wish to disable.** 

> ⚠️ setting the API keys and token to `user_provided` allows you to provide them safely from the webUI

> * For `CREDS_KEY`, `CREDS_IV` and `JWT_SECRET` use this tool: **[Credentials Generator](/toolkit/creds_generator)**
> * Run the tool a second time and use the new `JWT_SECRET` value for the `JWT_REFRESH_SECRET`

| Variables | Values |
| --- | --- |
| APP_TITLE | LibreChat |
| ALLOW_REGISTRATION | true |

## Deployment

**1.** When you're done filling the `secrets` and `variables`, click `Duplicate Space` in the bottom of that window

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/55d596a3-2be9-4e14-ac0d-0b493d463b1b)


**2.** The project will now build, this will take a couple of minutes

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/f9fd10e4-ae50-4b5f-a9b5-0077d9e4eaf6)


**3.** When ready, `Building` will change to `Running` 

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/91442e84-9c9e-4398-9011-76c479b6f272)

  And you will be able to access LibreChat!

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/cd5950d4-ecce-4f13-bbbf-b9109e462e10)

## Update
  To update LibreChat, simply select `Factory Reboot` from the ⚙️Settings menu

  ![image](https://github.com/fuegovic/LibreChat/assets/32828263/66f20129-0ffd-44f5-b91c-fcce1932112f)


## Conclusion
  You can now access it with from the current URL. If you want to access it without the Hugging Face overlay, you can modify this URL template with your info:

  `https://username-projectname.hf.space/` 
  
  e.g. `https://cooluser-librechat.hf.space/`

**🎉 Congratulation, you've successfully deployed LibreChat on Hugging Face! 🤗**


## Meilisearch Setup (Optional)

To enable the search functionality in LibreChat, you'll need to deploy and configure a Meilisearch instance.  Here's how:

**1. Duplicate the Meilisearch Space:**

Visit this link: [https://huggingface.co/spaces/LibreChat/meilisearch](https://huggingface.co/spaces/LibreChat/meilisearch) and click "Duplicate this Space".

**2. Configure the Meilisearch Space:**

   *   **Visibility:** Set the visibility to "public".

   *   **MEILI_MASTER_KEY:** Generate a secure 16-character master key. You can use a tool like [https://randomkeygen.com/](https://randomkeygen.com/) to generate a random key.  Set this key as the value for the `MEILI_MASTER_KEY` environment variable in the Meilisearch space.  *Important: Keep this key secure!*

   *   **MEILI_ENV:** Set the `MEILI_ENV` environment variable to `production`.

**3. Duplicate the Space:**

Click the "Duplicate Space" button.

**4. Configure LibreChat to use Meilisearch:**

   *   **Edit the Dockerfile:** Go to your LibreChat space (the one you duplicated from the main LibreChat template). Navigate to "Files" -> "Dockerfile" and click "Edit".

   *   **Uncomment and Modify Lines:**  Uncomment/edit the following lines in the Dockerfile.  These lines will contain `ENV SEARCH` and `ENV MEILI_*`.  Make sure to replace `<YOUR_MEILISEARCH_SPACE_URL>` with the actual URL of your Meilisearch deployment on Hugging Face Spaces. It should look something like `https://<your-username>-meilisearch.hf.space/`.  *Update the username to match your username!*

       ```dockerfile
       ENV SEARCH=true
       ENV MEILI_NO_ANALYTICS=true
       ENV MEILI_HOST=<YOUR_MEILISEARCH_SPACE_URL>
       ```

   *   **Commit Changes:** Commit your changes to the `main` branch.

**5. Add the `MEILI_MASTER_KEY` Secret to LibreChat:**

   *   Go to your LibreChat space's settings (the LibreChat deployment, not the Meilisearch one).

   *   Click "New secret".

   *   **Name:** Enter `MEILI_MASTER_KEY`.

   *   **Value:**  Enter the *same* master key you used when setting up the Meilisearch space.

**6. Verify the Setup:**

   After LibreChat rebuilds and starts running, you should see a search option in the top left of the LibreChat interface.  If you don't see it, double-check that you've followed all the steps correctly.


# Railway (one-click) (https://www.librechat.ai/docs/remote/railway)

Railway provides a one-click install option for deploying LibreChat, making the process even simpler. Here's how you can do it:

## Steps

### **Visit the LibreChat repository**

Go to the [LibreChat repository](https://github.com/danny-avila/LibreChat) on GitHub.

### **Create a Railway account**

[Sign up for a Railway account](https://railway.app?referralCode=HI9hWz&utm_medium=integration&utm_source=docs&utm_campaign=librechat) if you don't already have one (this link includes a referral code that supports the LibreChat project).

### **Click the "Deploy on Railway" button**

<p align="left">
    <a href="https://railway.com/deploy/librechat-official?referralCode=HI9hWz&utm_medium=integration&utm_source=docs&utm_campaign=librechat">
        <img src="https://railway.com/button.svg" alt="Deploy on Railway" height="40"/>
    </a>
</p>

(The button is also available in the repository's README file)


### **Configure environment variables**

Railway will automatically detect the required environment variables for LibreChat. Review the configuration of the three containers and click `Save Config` after reviewing each of them.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/4417e997-621c-44b6-8d2d-94d7e4e1a2bf)

The default configuration will get you started, but for more advanced features, you can consult our documentation on the subject: [Environment Variables](/docs/configuration/dotenv)

### **Deploy**

Once you've filled in the required environment variables, click the "Deploy" button. Railway will handle the rest, including setting up a PostgreSQL database and building/deploying your LibreChat instance.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/d94e20c6-0ae7-42af-8937-7fbd34d63a3b)

### **Access your LibreChat instance**

After the deployment is successful, Railway will provide you with a public URL where you can access your LibreChat instance.

That's it! You have successfully deployed LibreChat on Railway using the one-click install process. You can now start using and customizing your LibreChat instance as needed.

## Additional Tips

- Regularly check the LibreChat repository for updates and redeploy your instance to receive the latest features and bug fixes.
- You can find the "redeploy" option in Railway after you login by clicking the 3 dots to the right of "view logs" button

For more detailed instructions and troubleshooting, refer to the official LibreChat documentation and the Railway guides.


# Cloudflare (https://www.librechat.ai/docs/remote/cloudflare)

## Registering Your Domain

### Step 1: Choose Your Domain Name
- Select a domain name that aligns with your brand identity and is memorable. Avoid complex spellings.
- Use online tools from domain registrars like Cloudflare, Namecheap, GoDaddy, etc., to check if your preferred domain name is available.

### Step 2: Select a Domain Registrar
- Evaluate registrars based on their pricing, customer support, additional features (such as email, SSL certificates), and user reviews.
- Pay special attention to privacy protection services (WHOIS privacy), as this helps keep your personal information private.

### Step 3: Purchase and Register the Domain
- Follow the registrar's purchase process, which involves providing your contact information and completing the payment.
- Thoroughly read the terms of service and note the domain's expiration date to avoid unexpected lapses.

## Configuring Cloudflare Tunnels

### Step 1: Add Your Domain to Cloudflare
- If your domain was purchased via Cloudflare, this step is skipped as your domain is already configured to use Cloudflare's nameservers.
- Log into your Cloudflare account and add your new domain. Follow the instructions to replace your domain's nameservers with those provided by Cloudflare, which is necessary for activating Cloudflare's services.

### Step 3: Create a Tunnel

<Cards>
   <Cards.Card href="https://one.dash.cloudflare.com/" title="Step 1" image arrow>
      ![image](https://github.com/danny-avila/LibreChat/assets/32828263/ae54133f-34ed-476d-9004-d269a8707609)
   </Cards.Card>
   <Cards.Card href="https://one.dash.cloudflare.com/" title="Step 2" image arrow>
      ![image](https://github.com/danny-avila/LibreChat/assets/32828263/643e878e-e418-48aa-ac53-6c144ed463f0)
   </Cards.Card>
   <Cards.Card href="https://one.dash.cloudflare.com/" title="Step 3" image arrow>
      ![image](https://github.com/danny-avila/LibreChat/assets/32828263/e219fd49-baad-4da0-84e7-09e754068c57)
   </Cards.Card>
</Cards>

![image](https://github.com/danny-avila/LibreChat/assets/32828263/c851d405-1e90-4ad1-b65e-03cee5ae7111)

#### Name your Tunnel

![image](https://github.com/danny-avila/LibreChat/assets/32828263/212b8b95-5901-40d4-bc46-3b3b3997767e)

#### Install Cloudflare's `cloudflared` on Your Server

![image](https://github.com/danny-avila/LibreChat/assets/32828263/82520d5c-8524-415f-82ae-c297bde3288d)

<Callout type="tip" title="Tip">
- For continuous operation, consider setting up `cloudflared` to run as a service on your system. This ensures the tunnel remains active after reboots and crashes.
</Callout>

- Download and install the [`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) tool from Cloudflare's official site onto your server. This software will facilitate the secure connection between your domain and the internal services.
- Authenticate `cloudflared` using your Cloudflare account credentials to link it to your domain.

#### Configure your Tunnel

- Initiate a tunnel using either the Cloudflare dashboard under the "Tunnels" section

![image](https://github.com/danny-avila/LibreChat/assets/32828263/0232a545-115a-4c91-99e3-1240542bbea2)


### Step 6: Verify the Tunnel
- Test the connection by accessing your domain/subdomain in a web browser, ensuring it resolves to your server via the Cloudflare Tunnel without errors.
- Monitor your tunnel's performance and status directly from the Cloudflare dashboard under the "Tunnels" section.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/2ad741d5-8cdd-46c6-982c-7fa05a5858a8)

## Conclusion
By securing your domain registration and configuring a Cloudflare Tunnel, you strengthen the security and reliability of connecting to your network services. Regularly review and update your domain and tunnel settings to adapt to any new requirements or changes in network configuration. Stay proactive about maintaining your online presence and security stance.

# NGINX (https://www.librechat.ai/docs/remote/nginx)

This guide covers the essential steps for securing your LibreChat deployment with an SSL/TLS certificate for HTTPS, setting up Nginx as a reverse proxy, and configuring your domain.

## Prerequisites

1. A cloud server (e.g., AWS, Google Cloud, Azure, Digital Ocean).
2. A registered domain name.
3. Terminal access to your cloud server.
4. Node.js `v24.16.0` and npm `v11.16.0` if you run LibreChat directly on the host.

## Initial Setup

### Pointing Your Domain to Your Server

Before proceeding with certificate acquisition, point your domain to your cloud server's IP address. This step is foundational and must precede SSL certificate setup due to the time DNS records may require to propagate globally.

1. Log in to your domain registrar's control panel.
2. Navigate to DNS settings.
3. Create an `A record` pointing your domain to the IP address of your cloud server.
4. Wait for the DNS changes to propagate globally (you can check by pinging your domain: `ping your_domain.com`).

## Obtain an SSL/TLS Certificate

To secure your LibreChat application with HTTPS, you'll need an SSL/TLS certificate. Let's Encrypt offers free certificates:

1. Install Certbot:
    - For Ubuntu: `sudo apt-get install certbot python3-certbot-nginx`
    - For CentOS: `sudo yum install certbot python2-certbot-nginx`

2. Obtain the Certificate:
    - Run `sudo certbot --nginx` to obtain and install the certificate automatically for Nginx.
    - Follow the on-screen instructions. Certbot will ask for information and complete the validation process.
    - Once successful, Certbot will store your certificate files.

## Set Up Nginx as a Reverse Proxy

Nginx acts as a reverse proxy, forwarding client requests to your LibreChat application. There are two deployment options:

### Option A: Using the `deploy-compose.yml` Docker Compose (Recommended)

The `deploy-compose.yml` file includes an Nginx container and uses the `client/nginx.conf` file for Nginx configuration. However, since `sudo certbot --nginx` extracts the certificate to the host configuration, you need to duplicate the certificate to the Docker containers.

1. Update `client/nginx.conf` with your domain and certificate paths.
2. Update `deploy-compose.yml` in the `client` section to mount the certificate files from the host:

```yaml
client:
  # ...
  volumes:
    - ./client/nginx.conf:/etc/nginx/conf.d/default.conf
    - /etc/letsencrypt/live/<your-domain>:/etc/letsencrypt/live/<your-domain>
    - /etc/letsencrypt/archive/<your-domain>:/etc/letsencrypt/archive/<your-domain>
    - /etc/letsencrypt/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf
    - /etc/letsencrypt/ssl-dhparams.pem:/etc/letsencrypt/ssl-dhparams.pem
```

3. Stop any running instance: `npm run stop:deployed`
4. Commit the changes to a new Git branch.
5. Rebase the deployed instance: `npm run rebase:deployed`

### Option B: Host-based Deployment without Docker

If you're not using Docker, you can install and configure Nginx directly on the host:

1. Install Nginx:
    - Ubuntu: `sudo apt-get install nginx`
    - CentOS: `sudo yum install nginx`

2. Start Nginx: `sudo systemctl start nginx`

3. Open the Nginx configuration file: `sudo nano /etc/nginx/sites-available/default`

4. Replace the file content with the following, ensuring to replace `your_domain.com` with your domain and `app_port` with your application's port:

```nginx filename="/etc/nginx/sites-available/default"
server {
    listen 80;
    server_name your_domain.com;

    location / {
        proxy_pass http://localhost:app_port;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
```

5. Check the Nginx configuration: `sudo nginx -t`
6. Reload Nginx: `sudo systemctl reload nginx`

## Run the Application

1. Navigate to your application's directory:

```bash filename="Replace 'LibreChat' with your actual application directory.
cd LibreChat
```

2. Start your application using Docker Compose:

```bash filename="Start your application"
sudo docker compose -f ./deploy-compose.yml up -d
```

## Renewing certificates when using nginx

If you set up nginx using the recommended Option A above, use these steps to renew the certificates:

1. Navigate to your application's directory
```bash filename="Replace 'LibreChat' with your actual application directory.
cd LibreChat
```

2. Stop your running Docker containers

```bash filename="Stop your application"
sudo docker compose -f ./deploy-compose.yml down -d
```

3. renew certificates

```bash
sudo certbot renew
```

4. Restart your application

```bash filename="Start your application"
sudo docker compose -f ./deploy-compose.yml up -d
```

Note: certbot might restart the host's nginx. You can kill it with `sudo pkill nginx` 

## Web Application Firewall

Nginx can be configured to act as a web application firewall (WAF) by leveraging the OWASP Core Rule Set (CRS), which provides a robust set of rules to protect against common web application vulnerabilities and attacks. Using OWASP CRS with Nginx can enhance the security of your LibreChat deployment by adding an additional layer of protection.

1. Install OWASP CRS:

    - Ubuntu: `sudo apt-get install nginx-modsecurity-crs`

2. Enable ModSecurity in Nginx:
    - Open your Nginx configuration file (e.g., `/etc/nginx/nginx.conf`).
    - Add the following lines inside the `http` block:

      ```yaml filename="nginx.conf"
      modsecurity on;
      modsecurity_rules_file /usr/share/nginx/modsecurity-crs/nginx-modsecurity.conf;
      ```

3. Configure OWASP CRS:
    - The OWASP CRS package typically includes a configuration file (e.g., `/etc/nginx/modsecurity.d/nginx-modsecurity.conf`) where you can adjust various settings and rulesets based on your requirements.

4. Reload Nginx:
    - `sudo systemctl reload nginx`

By enabling OWASP CRS in your Nginx configuration, you can leverage the comprehensive set of rules provided by the project to detect and mitigate various web application vulnerabilities and attacks, such as SQL injection, cross-site scripting (XSS), remote file inclusion, and more.

## Static File Caching and Compression

LibreChat now supports static file caching and compression natively. If you're using NGINX to handle compression, you should disable compression in LibreChat to avoid redundant processing. You can do this by setting the `DISABLE_COMPRESSION` environment variable to `true` in your LibreChat configuration.

```.env
# .env file
DISABLE_COMPRESSION=true
```

This will prevent LibreChat from compressing static files, allowing NGINX to handle compression more efficiently.

For more information on static file handling in LibreChat, including caching options, refer to the [Static File Handling](/docs/configuration/dotenv#static-file-handling) documentation.


# ngrok (https://www.librechat.ai/docs/remote/ngrok)

To use ngrok for tunneling your local server to the internet, follow these steps:

## Sign up

1. Go to **[https://ngrok.com/](https://ngrok.com/)** and sign up for an account.

## Docker Installation

1. Copy your auth token from: **[https://dashboard.ngrok.com/get-started/your-authtoken](https://dashboard.ngrok.com/get-started/your-authtoken)**
2. Open a terminal and run the following command: `docker run -d -it -e NGROK_AUTHTOKEN=<your token> ngrok/ngrok http 80`

## Windows Installation

1. Download the ZIP file from: **[https://ngrok.com/download](https://ngrok.com/download)**
2. Extract the contents of the ZIP file using 7zip or WinRar.
3. Run `ngrok.exe`.
4. Copy your auth token from: **[https://dashboard.ngrok.com/get-started/your-authtoken](https://dashboard.ngrok.com/get-started/your-authtoken)**
5. In the `ngrok.exe` terminal, run the following command: `ngrok config add-authtoken <your token>`
6. If you haven't done so already, start LibreChat normally.
7. In the `ngrok.exe` terminal, run the following command: `ngrok http 3080`

You will see a link that can be used to access LibreChat.
![ngrok-1](https://github.com/danny-avila/LibreChat/assets/32828263/3cb4b063-541f-4f0a-bea8-a04dd36e6bf4)

## Linux Installation

1. Copy the command from: **[https://ngrok.com/download](https://ngrok.com/download)** choosing the **correct** architecture.
2. Run the command in the terminal
3. Copy your auth token from: **[https://dashboard.ngrok.com/get-started/your-authtoken](https://dashboard.ngrok.com/get-started/your-authtoken)**
4. run the following command: `ngrok config add-authtoken <your token>`
5. If you haven't done so already, start LibreChat normally.
6. run the following command: `ngrok http 3080`

## Mac Installation

1. Download the ZIP file from: **[https://ngrok.com/download](https://ngrok.com/download)**
2. Extract the contents of the ZIP file using a suitable Mac application like Unarchiver.
3. Open Terminal.
4. Navigate to the directory where you extracted ngrok using the `cd` command.
5. Run ngrok by typing `./ngrok`.
6. Copy your auth token from: **[https://dashboard.ngrok.com/get-started/your-authtoken](https://dashboard.ngrok.com/get-started/your-authtoken)**
7. In the terminal where you ran ngrok, enter the following command: `ngrok authtoken <your token>`
8. If you haven't done so already, start LibreChat normally.
9. In the terminal where you ran ngrok, enter the following command: `./ngrok http 3080`


# Traefik (https://www.librechat.ai/docs/remote/traefik)

[Traefik](https://traefik.io/) is a modern HTTP reverse proxy and load balancer that makes it easy to deploy and manage your services. If you're running LibreChat on Docker, you can use Traefik to expose your instance securely over HTTPS with automatic SSL certificate management.

## Prerequisites

- Docker and Docker Compose installed on your system
- A domain name pointing to your server's IP address

## Configuration

### Configure Traefik and LibreChat

    In your docker-compose.override.yml file, add the following configuration:

```yaml filename="docker-compose.override.yml"
version: '3'

services:
    api:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.librechat.rule=Host(`your.domain.name`)"
        - "traefik.http.routers.librechat.entrypoints=websecure"
        - "traefik.http.routers.librechat.tls.certresolver=leresolver"
        - "traefik.http.services.librechat.loadbalancer.server.port=3080"
      networks:
        - librechat_default
      volumes:
        - ./librechat.yaml:/app/librechat.yaml
  
    traefik:
      image: traefik:v3.6
      ports:
        - "80:80"
        - "443:443"
      volumes:
        - "/var/run/docker.sock:/var/run/docker.sock:ro"
        - "./letsencrypt:/letsencrypt"
      networks:
        - librechat_default
      command:
        - "--log.level=DEBUG"
        - "--api.insecure=true"
        - "--providers.docker=true"
        - "--providers.docker.exposedbydefault=false"
        - "--entrypoints.web.address=:80"
        - "--entrypoints.websecure.address=:443"
        - "--certificatesresolvers.leresolver.acme.tlschallenge=true"
        - "--certificatesresolvers.leresolver.acme.email=your@email.com"
        - "--certificatesresolvers.leresolver.acme.storage=/letsencrypt/acme.json"

# other configs here #

# NOTE: This needs to be at the bottom of your docker-compose.override.yml
networks:
  librechat_default:
    external: true
```

  Replace `your@email.com` with your email address for Let's Encrypt certificate notifications.

  see: [Docker Override](/docs/configuration/docker_override) for more info.

### Start the containers

  ```bash filename="Start the containers"
  docker compose up -d
  ```

  This will start Traefik and LibreChat containers. Traefik will automatically obtain an SSL/TLS certificate from Let's Encrypt and expose your LibreChat instance securely over HTTPS.

You can now access your LibreChat instance at `https://your.domain.name`. Traefik will handle SSL/TLS termination and reverse proxy requests to your LibreChat container.

## Additional Notes

- The Traefik configuration listens on ports 80 and 443 for HTTP and HTTPS traffic, respectively. Ensure that these ports are open on your server's firewall.
- Traefik stores SSL/TLS certificates in the `./letsencrypt` directory on your host machine. You may want to back up this directory periodically.
- For more advanced configuration options, refer to the official Traefik documentation: [https://doc.traefik.io/](https://doc.traefik.io/)

## Static File Caching and Compression

LibreChat now supports static file caching and compression natively. If you're using Traefik to handle compression, you should disable compression in LibreChat to avoid redundant processing. You can do this by setting the `DISABLE_COMPRESSION` environment variable to `true` in your LibreChat configuration.

```.env
# .env file
DISABLE_COMPRESSION=true
```

This will prevent LibreChat from compressing static files, allowing Traefik to handle compression more efficiently.

For more information on static file handling in LibreChat, including caching options, refer to the [Static File Handling](/docs/configuration/dotenv#static-file-handling) documentation.


# Overview (https://www.librechat.ai/docs/configuration)

LibreChat uses four main configuration files. Each controls a different aspect of the application -- from environment variables to custom AI endpoints to Docker service overrides.

## Common Change Workflow

Most configuration changes follow the same pattern:

1. Edit `.env` for secrets, API keys, and server-level feature flags.
2. Edit `librechat.yaml` for custom endpoints, model specs, interface settings, MCP servers, agents, and advanced app behavior.
3. For Docker, make sure `librechat.yaml` is mounted through `docker-compose.override.yml` before expecting LibreChat to read it.
4. Restart LibreChat after every configuration change.
5. Check the API logs if the change does not appear in the UI.

For example, to enable OpenRouter you add `OPENROUTER_KEY` to `.env`, add an OpenRouter endpoint in `librechat.yaml`, make sure Docker mounts `librechat.yaml`, restart, then select OpenRouter from the endpoint selector.

## Configuration Files

<FileTree>
  <FileTree.Folder name="LibreChat (project root)" defaultOpen>
    <FileTree.File name=".env" active />
    <FileTree.File name="librechat.yaml" active />
    <FileTree.File name="docker-compose.yml" />
    <FileTree.File name="docker-compose.override.yml" active />
  </FileTree.Folder>
</FileTree>

**`.env`** -- Server-level settings: API keys, database connection strings, feature flags, and authentication secrets. This is the primary configuration file for most deployments. See the [.env reference](/docs/configuration/dotenv) for all available variables.

**`librechat.yaml`** -- Custom AI endpoints, model settings, interface options, and advanced features like MCP servers and agents. This file is optional -- LibreChat works with defaults if it does not exist. See the [librechat.yaml guide](/docs/configuration/librechat_yaml) for setup instructions.

**`docker-compose.yml`** -- Defines the Docker services (API server, database, search). Do not edit this file directly -- use an override file instead so your changes survive updates.

**`docker-compose.override.yml`** -- Your local customizations to Docker services: volume mounts, port mappings, environment overrides. Docker Compose merges this with the main file automatically. See the [Docker override guide](/docs/configuration/docker_override).

## Applying Changes

<Callout type="warning" title="Restart Required">

After editing any configuration file, you must restart LibreChat for changes to take effect.

<Tabs items={['Docker', 'Local']}>
  <Tabs.Tab>

```bash
docker compose down && docker compose up -d
```

  </Tabs.Tab>
  <Tabs.Tab>

Stop the running process (Ctrl+C) and restart:

```bash
npm run backend
```

  </Tabs.Tab>
</Tabs>

</Callout>

## Next Steps

<Cards num={3}>
  <Cards.Card title="librechat.yaml Setup" href="/docs/configuration/librechat_yaml" arrow>
    Create and configure the main LibreChat config file
  </Cards.Card>
  <Cards.Card title="Docker Setup" href="/docs/quick_start/local_setup" arrow>
    Install and run LibreChat with Docker
  </Cards.Card>
  <Cards.Card title=".env Reference" href="/docs/configuration/dotenv" arrow>
    Environment variables for server configuration
  </Cards.Card>
</Cards>


# Environment Variables (https://www.librechat.ai/docs/configuration/dotenv)

Welcome to the comprehensive guide for configuring your application's environment with the `.env` file. This document is your one-stop resource for understanding and customizing the environment variables that will shape your application's behavior in different contexts.

While the default settings provide a solid foundation for a standard `docker` installation, delving into this guide will unveil the full potential of LibreChat. This guide empowers you to tailor LibreChat to your precise needs. Discover how to adjust language model availability, integrate social logins, manage the automatic moderation system, and much more. It's all about giving you the control to fine-tune LibreChat for an optimal user experience.

> **Reminder: Please restart LibreChat for the configuration changes to take effect**

Alternatively, you can create a new file named `docker-compose.override.yml` in the same directory as your main `docker-compose.yml` file for LibreChat, where you can set your .env variables as needed under `environment`, or modify the default configuration provided by the main `docker-compose.yml`, without the need to directly edit or duplicate the whole file.

For more info see:

- Our quick guide:
  - **[Docker Override](/docs/configuration/docker_override)**

- The official docker documentation:
  - **[docker docs - understanding-multiple-compose-files](https://docs.docker.com/compose/how-tos/multiple-compose-files/extends/)**
  - **[docker docs - merge-compose-files](https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/)**
  - **[docker docs - specifying-multiple-compose-files](https://docs.docker.com/compose/reference/#specifying-multiple-compose-files)**

- You can also view an example of an override file for LibreChat in your LibreChat folder and on GitHub:
  - **[docker-compose.override.example](https://github.com/danny-avila/LibreChat/blob/main/docker-compose.override.yml.example)**

---

## Server Configuration

### Port

- The server listens on a specific port.
- The `PORT` environment variable sets the port where the server listens. By default, it is set to `3080`.

<OptionTable
  options={[
    ['HOST', 'string', 'Specifies the host.', 'HOST=localhost'],
    ['PORT', 'number', 'Specifies the port.', 'PORT=3080'],
  ]}
/>

### HTTP Server Timeouts

These optional values configure the Node.js HTTP server. Leave them unset to retain Node's defaults. A value of `0` disables that timeout.

<OptionTable
  options={[
    [
      'HTTP_KEEP_ALIVE_TIMEOUT_MS',
      'integer',
      'Idle keep-alive timeout in milliseconds. Set this above the load balancer idle timeout to avoid reused connections racing server closure. Node default: 5000.',
      '# HTTP_KEEP_ALIVE_TIMEOUT_MS=70000',
    ],
    [
      'HTTP_KEEP_ALIVE_TIMEOUT_BUFFER_MS',
      'integer',
      'Additional socket timeout buffer in milliseconds. Node default: 1000.',
      '# HTTP_KEEP_ALIVE_TIMEOUT_BUFFER_MS=5000',
    ],
    [
      'HTTP_HEADERS_TIMEOUT_MS',
      'integer',
      'Time allowed to receive complete request headers. Node default: 60000.',
      '# HTTP_HEADERS_TIMEOUT_MS=80000',
    ],
    [
      'HTTP_REQUEST_TIMEOUT_MS',
      'integer',
      'Time allowed to receive the complete request. Node default: 300000.',
      '# HTTP_REQUEST_TIMEOUT_MS=300000',
    ],
  ]}
/>

Values must be non-negative safe integers; invalid values are ignored. When both header and request timeouts are enabled, LibreChat clamps the header timeout to the request timeout if it is higher. Node checks header and request expiry on a 30-second connection sweep, so values below `30000` are not enforced at exact millisecond precision. Bun currently accepts these settings but does not enforce them; LibreChat logs a warning when the API server runs under Bun.

### Trust proxy

Use the address that is at most n number of hops away from the Express application.
req.socket.remoteAddress is the first hop, and the rest are looked for in the X-Forwarded-For header from right to left.
A value of 0 means that the first untrusted address would be req.socket.remoteAddress, i.e. there is no reverse proxy.
The `TRUST_PROXY` environment variable default is set to `1`.

Refer to [Express.js - trust proxy](https://expressjs.com/en/guide/behind-proxies.html) for more information about this.

<OptionTable
  options={[['TRUST_PROXY', 'number', 'Specifies the number of hops.', 'TRUST_PROXY=1']]}
/>

### Trusted Tenant Header

`TRUST_TENANT_HEADER` controls whether LibreChat accepts `X-Tenant-Id` before authentication on the `/oauth`, `/api/auth`, and `/api/share` route trees. It is disabled by default; while disabled, those routes ignore the header.

<OptionTable
  options={[
    [
      'TRUST_TENANT_HEADER',
      'boolean',
      'Trust X-Tenant-Id on pre-authentication routes. Enable only behind a trusted proxy that replaces client-supplied values.',
      'TRUST_TENANT_HEADER=false',
    ],
  ]}
/>

<Callout type="warning" title="Do not trust client-supplied tenant headers">
  Enable this setting only when a trusted reverse proxy strips every incoming `X-Tenant-Id` value and sets the authoritative tenant ID itself. Otherwise, an untrusted client could choose the tenant context used before authentication. LibreChat logs a security warning when the setting is enabled.
</Callout>

When `TENANT_ISOLATION_STRICT` is enabled but `TRUST_TENANT_HEADER` is disabled, LibreChat warns at startup that pre-authentication tenant headers will be ignored.

### Credentials Configuration

LibreChat uses `CREDS_KEY` and `CREDS_IV` to encrypt supported credentials stored in the database. Configure fixed, unique values for production and share the same values across every replica.

<OptionTable
  options={[
    [
      'CREDS_KEY',
      'string',
      '32-byte key (64 hexadecimal characters) for securely storing credentials.',
      'CREDS_KEY=',
    ],
    [
      'CREDS_IV',
      'string',
      '16-byte IV (32 hexadecimal characters) for securely storing credentials.',
      'CREDS_IV=',
    ],
    [
      'LIBRECHAT_TEMP_CREDENTIALS_PATH',
      'string',
      'Path used to persist automatically generated temporary credentials. Defaults to `.env.temp` in the process working directory.',
      '# LIBRECHAT_TEMP_CREDENTIALS_PATH=/app/data/.env.temp',
    ],
  ]}
/>

When any of `CREDS_KEY`, `CREDS_IV`, `JWT_SECRET`, or `JWT_REFRESH_SECRET` is blank, LibreChat first reuses a valid value from the temporary credentials file. If none exists, it generates a cryptographically random value and writes the file with owner-only permissions. Explicit environment values always take precedence. LibreChat refuses to use `.env` or `.env.example` as the generated file path, preventing accidental replacement of either configuration file.

LibreChat also refuses to start when `JWT_SECRET` or `JWT_REFRESH_SECRET` still uses one of the retired published defaults, whether the value comes from the environment or the temporary credentials file. Generate unique replacements rather than carrying old sample secrets into a deployment.

The bundled Docker Compose files persist `/app/data/.env.temp` in the `librechat-data` volume, so a single Compose deployment can restart without changing these generated values. If the file cannot be persisted, the values are process-local and sessions or encrypted records may become inaccessible after restart.

<Callout type="warning" title="Set permanent values for production">
  Temporary generation is a bootstrap convenience, not a credential-rotation system. Before production use, generate permanent values with the [Credentials Generator](/toolkit/creds_generator), store them in your secret manager, and provide the same values to every replica. Changing an established key does not re-encrypt existing records. LibreChat records credential fingerprints in the database and warns when active values drift; do not overwrite that marker instead of performing a controlled migration.
</Callout>

### Security Headers and Content Security Policy

LibreChat sends baseline HSTS, framing, content-type, opener, resource, and referrer headers by default. `SECURITY_HEADERS=false` disables all of them and is also the global CSP kill switch.

Nonce-based CSP is separately opt-in with `CSP_ENABLED=true` and defaults to report-only mode. Start with `CSP_REPORT_ONLY=true`, inspect violations, and add only the origins your deployment requires before enforcing. When CSP is active, the SPA shell is always sent with `Cache-Control: no-store` so its per-response nonce cannot be reused from cache.

See [HTTP Security Headers](/docs/configuration/security_headers) for every baseline and CSP variable, accepted values, defaults, source-list overrides, and rollout guidance.

### Static File Handling

<OptionTable
  options={[
    [
      'STATIC_CACHE_MAX_AGE',
      'string',
      'Cache-Control max-age in seconds',
      'STATIC_CACHE_MAX_AGE=172800',
    ],
    [
      'STATIC_CACHE_S_MAX_AGE',
      'string',
      'Cache-Control s-maxage in seconds for shared caches (CDNs and proxies)',
      'STATIC_CACHE_S_MAX_AGE="86400"',
    ],
    [
      'DISABLE_COMPRESSION',
      'boolean',
      'Disables compression for static files.',
      'DISABLE_COMPRESSION=false',
    ],
    [
      'ENABLE_IMAGE_OUTPUT_GZIP_SCAN',
      'boolean',
      'Enables serving gzipped versions of uploaded images if present in the same folder.',
      'ENABLE_IMAGE_OUTPUT_GZIP_SCAN=true',
    ],
    [
      'ENABLE_STATIC_ASSET_BROTLI',
      'boolean',
      'Enables serving precompressed Brotli versions of static app assets when available.',
      'ENABLE_STATIC_ASSET_BROTLI=true',
    ],
  ]}
/>

**Behaviour:**

Sets the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) headers for static files. These configurations only trigger when the `NODE_ENV` is set to `production`.

- Uncomment `STATIC_CACHE_MAX_AGE` to change the local `max-age` for static files. By default this is set to 2 days (172800 seconds).
- Uncomment `STATIC_CACHE_S_MAX_AGE` to set the `s-maxage` for shared caches (CDNs and proxies). By default this is set to 1 day (86400 seconds).
- Uncomment `DISABLE_COMPRESSION` to disable compression for static files. By default, compression is enabled.
- Uncomment `ENABLE_IMAGE_OUTPUT_GZIP_SCAN` to enable scanning and serving of gzipped version of images if they have been pre-compressed in the same folder, with the same name and a .gz extension. By default, gzip scan for uploaded images is disabled.
- Uncomment `ENABLE_STATIC_ASSET_BROTLI` to serve precompressed `.br` versions of static app assets when they exist. When enabled, Brotli is preferred before gzip for API-served static files.

<Callout type="warning" title="Warning">
  - This only affects static files served by the API server and is not applicable to _Firebase_,
  _NGINX_, or any other configurations.
</Callout>

### Index HTML Cache Control

<OptionTable
  options={[
    [
      'INDEX_CACHE_CONTROL',
      'string',
      'Cache-Control header for index.html',
      'INDEX_CACHE_CONTROL=no-cache, no-store, must-revalidate',
    ],
    ['INDEX_PRAGMA', 'string', 'Pragma header for index.html', 'INDEX_PRAGMA=no-cache'],
    ['INDEX_EXPIRES', 'string', 'Expires header for index.html', 'INDEX_EXPIRES=0'],
  ]}
/>

**Behaviour:**

Controls caching headers specifically for the index.html response. By default, these settings prevent caching to ensure users always get the latest version of the application.

<Callout type="note" title="Note">
  Unlike static assets which are cached for performance, the index.html file's cache headers are
  configured separately to ensure users always get the latest application shell.
</Callout>

### MongoDB Database

<OptionTable
  options={[
    [
      'MONGO_URI',
      'string',
      'Specifies the MongoDB URI.',
      'MONGO_URI=mongodb://127.0.0.1:27017/LibreChat',
    ],
  ]}
/>

Change this to your MongoDB URI if different. You should add `LibreChat` or your own `APP_TITLE` as the database name in the URI.

If you are using an online database, the URI format is `mongodb+srv://<username>:<password>@<host>/<database>?<options>`. Your `MONGO_URI` should look like this:

- `mongodb+srv://username:password@host.mongodb.net/LibreChat?retryWrites=true` (`retryWrites` is the only option you need when using the online database.)

#### MongoDB Connection Pool Configuration

<OptionTable
  options={[
    [
      'MONGO_MAX_POOL_SIZE',
      'number',
      'The maximum number of connections in the connection pool.',
      '# MONGO_MAX_POOL_SIZE=',
    ],
    [
      'MONGO_MIN_POOL_SIZE',
      'number',
      'The minimum number of connections in the connection pool.',
      '# MONGO_MIN_POOL_SIZE=',
    ],
    [
      'MONGO_MAX_CONNECTING',
      'number',
      'The maximum number of connections that may be in the process of being established concurrently by the connection pool.',
      '# MONGO_MAX_CONNECTING=',
    ],
    [
      'MONGO_MAX_IDLE_TIME_MS',
      'number',
      'The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed.',
      '# MONGO_MAX_IDLE_TIME_MS=',
    ],
    [
      'MONGO_WAIT_QUEUE_TIMEOUT_MS',
      'number',
      'The maximum time in milliseconds that a thread can wait for a connection to become available.',
      '# MONGO_WAIT_QUEUE_TIMEOUT_MS=',
    ],
  ]}
/>

#### MongoDB Schema Configuration

<OptionTable
  options={[
    [
      'MONGO_AUTO_INDEX',
      'boolean',
      'Set to false to disable automatic index creation for all models associated with this connection. When omitted, uses Mongoose default behavior.',
      '# MONGO_AUTO_INDEX=',
    ],
    [
      'MONGO_AUTO_CREATE',
      'boolean',
      'Set to false to disable Mongoose automatically calling createCollection() on every model created on this connection. When omitted, uses Mongoose default behavior.',
      '# MONGO_AUTO_CREATE=',
    ],
  ]}
/>

Amazon DocumentDB 5.0+ instance-based clusters are a supported target. DocumentDB requires `retryWrites=false` and TLS with the AWS CA bundle; elastic clusters are unsupported because they do not provide unique indexes. A typical URI has this form:

`mongodb://username:password@cluster:27017/librechat?tls=true&tlsCAFile=/path-to-ca/global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false`

DocumentDB 4.0 can run with reduced partial-unique-index enforcement. See [Amazon DocumentDB Compatibility](/docs/user_guides/mongodb#amazon-documentdb-compatibility) for the supported target and caveats.

See also:

- [MongoDB Atlas](/docs/configuration/mongodb/mongodb_atlas) for instructions on how to create an online MongoDB Atlas database (useful for use without Docker)
- [MongoDB Community Server](/docs/configuration/mongodb/mongodb_community) for instructions on how to create a local MongoDB database (without Docker)
- [MongoDB Authentication](/docs/configuration/mongodb/mongodb_auth) To enable explicit authentication for MongoDB in Docker.
- [Manage your database with Mongo Express](/blog/2023-11-30_mongoexpress) for securely accessing your Docker MongoDB database

### Application Domains

To configure LibreChat for local use or custom domain deployment, set the following environment variables:

<OptionTable
  options={[
    [
      'DOMAIN_CLIENT',
      'string',
      'Specifies the client-side domain.',
      'DOMAIN_CLIENT=http://localhost:3080',
    ],
    [
      'DOMAIN_SERVER',
      'string',
      'Specifies the server-side domain.',
      'DOMAIN_SERVER=http://localhost:3080',
    ],
    [
      'ADMIN_PANEL_URL',
      'string',
      'External admin panel base URL used for admin OAuth/SSO redirects and the admin-only Settings > General link. Do not include a trailing slash.',
      'ADMIN_PANEL_URL=https://admin.example.com/admin',
    ],
    [
      'ADMIN_PANEL_SESSION_SECRET',
      'string',
      'Required session encryption key for the bundled admin panel (min 32 characters). The docker-compose and deploy-compose admin-panel services read it as their SESSION_SECRET. Generate with `openssl rand -hex 32` before starting the stack.',
      'ADMIN_PANEL_SESSION_SECRET=<your-32-char-random-string>',
    ],
    [
      'ADMIN_PANEL_PORT',
      'number',
      'Host port for the bundled admin panel in the default docker-compose. In deploy-compose the panel is served at http://admin.localhost via nginx instead.',
      'ADMIN_PANEL_PORT=3000',
    ],
  ]}
/>

When deploying LibreChat to a custom domain, replace `http://localhost:3080` with your deployed URL

- e.g. `https://librechat.example.com`.

### Prevent Public Search Engines Indexing

By default, your website will not be indexed by public search engines (e.g. Google, Bing, …). This means that people will not be able to find your website through these search engines. If you want to make your website more visible and searchable, you can change the following setting to `false`

<OptionTable
  options={[
    [
      'NO_INDEX',
      'boolean',
      'Prevents public search engines from indexing your website.',
      'NO_INDEX=true',
    ],
  ]}
/>

❗**Note:** This method is not guaranteed to work for all search engines, and some search engines may still index your website or web page for other purposes, such as caching or archiving. Therefore, you should not rely solely on this method to protect sensitive or confidential information on your website or web page.

### Logging

LibreChat has built-in central logging, see [Logging System](/docs/configuration/logging) for more info.

#### Log Files

- Debug logging is enabled by default and crucial for development.
- To report issues, reproduce the error and submit logs from `./api/logs/debug-%DATE%.log` at: **[LibreChat GitHub Issues](https://github.com/danny-avila/LibreChat/issues)**
- Error logs are stored in the same location.

#### Environment Variables

<OptionTable
  options={[
    ['DEBUG_LOGGING', 'boolean', 'Keep debug logs active.', 'DEBUG_LOGGING=true'],
    [
      'DEBUG_CONSOLE',
      'boolean',
      'Enable verbose console/stdout logs in the same format as file debug logs.',
      'DEBUG_CONSOLE=false',
    ],
    [
      'CONSOLE_LOG_LEVEL',
      'string',
      'Set console verbosity to error, warn, info, http, verbose, debug, activity, silly, or silent. Defaults to info, or debug when DEBUG_CONSOLE=true; an explicit value takes precedence.',
      '# CONSOLE_LOG_LEVEL=info',
    ],
    [
      'LOG_TO_FILE',
      'boolean',
      'Set to false to disable file-backed Winston transports while keeping console logging available.',
      'LOG_TO_FILE=true',
    ],
    [
      'CONSOLE_JSON',
      'boolean',
      'Enable verbose JSON console/stdout logs suitable for cloud deployments like GCP/AWS.',
      'CONSOLE_JSON=false',
    ],
    [
      'CONSOLE_JSON_STRING_LENGTH',
      'number',
      'Configure the truncation size for string values in JSON console/stdout logs. Default: 255.',
      '# CONSOLE_JSON_STRING_LENGTH=255',
    ],
    [
      'LIBRECHAT_LOG_DIR',
      'string',
      'Custom directory for log files. Defaults to /app/logs (Docker) or api/logs (local dev).',
      '# LIBRECHAT_LOG_DIR=/custom/log/path',
    ],
    [
      'MEM_DIAG',
      'boolean',
      'Enable memory diagnostics — logs heap/RSS snapshots every 60 seconds. Auto-enabled when running with --inspect.',
      '# MEM_DIAG=true',
    ],
    [
      'AGENT_DEBUG_LOGGING',
      'boolean',
      'Enables verbose debug logging in the agent controller (token counts, context pruning diagnostics).',
      '# AGENT_DEBUG_LOGGING=true',
    ],
  ]}
/>

Note:

- `DEBUG_LOGGING` can be used with either `DEBUG_CONSOLE` or `CONSOLE_JSON` but not both.
- `DEBUG_CONSOLE` and `CONSOLE_JSON` are mutually exclusive.
- `CONSOLE_LOG_LEVEL=silent` disables console output without disabling file logging. Invalid values fall back to the normal default and emit a warning.
- `CONSOLE_JSON`: When handling console logs in cloud deployments (such as GCP or AWS), enabling this will dump the logs with a UTC timestamp and format them as JSON.
  - See: [feat: Add CONSOLE_JSON](https://github.com/danny-avila/LibreChat/pull/2146)

Note: `DEBUG_CONSOLE` is not recommended, as the outputs can be quite verbose, and so it's disabled by default.

### Permission

> UID and GID are numbers assigned by Linux to each user and group on the system. If you have permission problems, set here the UID and GID of the user running the Docker Compose command. The applications in the container will run with these UID/GID.

<OptionTable
  options={[
    ['UID', 'number', 'The user ID.', '# UID=1000'],
    ['GID', 'number', 'The group ID.', '# GID=1000'],
  ]}
/>

### Langfuse Tracing and Tenant Fanout

Use the standard Langfuse variables for one central observability project. The optional fanout deployment can additionally route tenant traces, media, and feedback scores to tenant-specific Langfuse projects. See [Langfuse Tracing](/docs/configuration/langfuse) for setup, in-app connection management, and architecture.

<OptionTable
  options={[
    ['LANGFUSE_PUBLIC_KEY', 'string', 'Public key for the central Langfuse project.', '# LANGFUSE_PUBLIC_KEY='],
    ['LANGFUSE_SECRET_KEY', 'string', 'Secret key for the central Langfuse project.', '# LANGFUSE_SECRET_KEY='],
    ['LANGFUSE_BASE_URL', 'string', 'Base URL for central tracing and feedback scores.', '# LANGFUSE_BASE_URL=https://cloud.langfuse.com'],
    ['LANGFUSE_PROJECT_ID', 'string', 'Stable central Langfuse project ID. When omitted, LibreChat discovers and caches it in the background for feedback routing.', '# LANGFUSE_PROJECT_ID='],
    ['LANGFUSE_TRACING_ENABLED', 'boolean', 'Set to false to disable Langfuse traces and feedback scores. Default: true.', '# LANGFUSE_TRACING_ENABLED=true'],
    ['LANGFUSE_SAMPLE_RATE', 'number', 'Deterministic trace-level sample rate from 0 to 1. Sampled-out traces do not receive feedback scores. Default: 1.', '# LANGFUSE_SAMPLE_RATE=1'],
    ['LANGFUSE_FANOUT_ENABLED', 'boolean', 'Enable routing through the optional fanout gateway.', '# LANGFUSE_FANOUT_ENABLED=false'],
    ['LANGFUSE_FANOUT_COLLECTOR_URL', 'string', 'Gateway URL used by the LibreChat API.', '# LANGFUSE_FANOUT_COLLECTOR_URL=http://langfuse-fanout-collector:4318'],
    ['LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED', 'boolean', 'Prevent the LibreChat SDK from creating media uploads for central or fallback collector traces. Tenant-routed media uploads are unchanged.', '# LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED=false'],
    ['LANGFUSE_FANOUT_LISTEN_ADDR', 'string', 'Gateway HTTP listen address. Default: :4318.', '# LANGFUSE_FANOUT_LISTEN_ADDR=:4318'],
    ['LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED', 'boolean', 'Emergency switch that disables tenant trace and score export while retaining central export.', '# LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED=false'],
    ['LANGFUSE_FANOUT_CENTRAL_BASE_URL', 'string', 'Central Langfuse base URL used by the gateway.', '# LANGFUSE_FANOUT_CENTRAL_BASE_URL=https://cloud.langfuse.com'],
    ['LANGFUSE_FANOUT_CENTRAL_AUTH_HEADER', 'string', 'Full Basic authorization header for central trace and media export.', '# LANGFUSE_FANOUT_CENTRAL_AUTH_HEADER=Basic <base64-public-colon-secret>'],
    ['LANGFUSE_FANOUT_CENTRAL_MEDIA_EXPORT_DISABLED', 'boolean', 'Disable central media fanout without disabling central traces.', '# LANGFUSE_FANOUT_CENTRAL_MEDIA_EXPORT_DISABLED=false'],
    ['LANGFUSE_FANOUT_TENANT_DESTINATIONS', 'string', 'Comma-separated destination key and base URL mappings allowed at startup.', '# LANGFUSE_FANOUT_TENANT_DESTINATIONS=eu=https://cloud.langfuse.com,us=https://us.cloud.langfuse.com'],
    ['LANGFUSE_FANOUT_TRACE_DESTINATION_KEYS', 'string', 'Destination keys supported by the collector routing configuration.', '# LANGFUSE_FANOUT_TRACE_DESTINATION_KEYS=eu,us,jp'],
    ['LANGFUSE_FANOUT_PUBLIC_URL', 'string', 'Gateway base URL used to create one-time media upload URLs.', '# LANGFUSE_FANOUT_PUBLIC_URL=http://langfuse-fanout-collector:4318'],
    ['LANGFUSE_FANOUT_TRACE_COLLECTOR_URL', 'string', 'Internal gateway-to-collector trace endpoint.', '# LANGFUSE_FANOUT_TRACE_COLLECTOR_URL=http://langfuse-fanout-otel:4319'],
    ['LANGFUSE_FANOUT_REDIS_URI', 'string', 'Redis URI for one-time media upload plans.', '# LANGFUSE_FANOUT_REDIS_URI=redis://langfuse-fanout-redis:6379'],
    ['LANGFUSE_FANOUT_REDIS_USERNAME', 'string', 'Optional Redis username for the fanout gateway.', '# LANGFUSE_FANOUT_REDIS_USERNAME='],
    ['LANGFUSE_FANOUT_REDIS_PASSWORD', 'string', 'Optional Redis password for the fanout gateway.', '# LANGFUSE_FANOUT_REDIS_PASSWORD='],
    ['LANGFUSE_FANOUT_REDIS_KEY_PREFIX', 'string', 'Redis key prefix for fanout media plans.', '# LANGFUSE_FANOUT_REDIS_KEY_PREFIX=langfuse-fanout'],
    ['LANGFUSE_FANOUT_OTEL_RECEIVER_ENDPOINT', 'string', 'Internal collector receiver bind address.', '# LANGFUSE_FANOUT_OTEL_RECEIVER_ENDPOINT=0.0.0.0:4319'],
    ['LANGFUSE_FANOUT_TENANT_EU_BASE_URL', 'string', 'Static EU destination URL for the included Compose collector.', '# LANGFUSE_FANOUT_TENANT_EU_BASE_URL=https://cloud.langfuse.com'],
    ['LANGFUSE_FANOUT_TENANT_US_BASE_URL', 'string', 'Static US destination URL for the included Compose collector.', '# LANGFUSE_FANOUT_TENANT_US_BASE_URL=https://us.cloud.langfuse.com'],
    ['LANGFUSE_FANOUT_TENANT_JP_BASE_URL', 'string', 'Static JP destination URL for the included Compose collector.', '# LANGFUSE_FANOUT_TENANT_JP_BASE_URL=https://jp.cloud.langfuse.com'],
    ['LANGFUSE_FANOUT_UPSTREAM_TIMEOUT', 'duration', 'Timeout for gateway requests to Langfuse and media upload URLs.', '# LANGFUSE_FANOUT_UPSTREAM_TIMEOUT=30s'],
    ['LANGFUSE_FANOUT_METRICS_SECRET', 'string', 'Bearer token required to scrape the gateway metrics endpoint.', '# LANGFUSE_FANOUT_METRICS_SECRET='],
    ['LANGFUSE_FANOUT_MEMORY_LIMIT_MIB', 'number', 'Collector memory limit in MiB.', '# LANGFUSE_FANOUT_MEMORY_LIMIT_MIB=256'],
    ['LANGFUSE_FANOUT_MEMORY_SPIKE_LIMIT_MIB', 'number', 'Collector memory spike allowance in MiB.', '# LANGFUSE_FANOUT_MEMORY_SPIKE_LIMIT_MIB=64'],
    ['LANGFUSE_FANOUT_BATCH_TIMEOUT', 'duration', 'Collector batch flush timeout.', '# LANGFUSE_FANOUT_BATCH_TIMEOUT=1s'],
    ['LANGFUSE_FANOUT_BATCH_SEND_SIZE', 'number', 'Collector batch send size.', '# LANGFUSE_FANOUT_BATCH_SEND_SIZE=128'],
    ['LANGFUSE_FANOUT_METADATA_CARDINALITY_LIMIT', 'number', 'Collector metadata cardinality limit.', '# LANGFUSE_FANOUT_METADATA_CARDINALITY_LIMIT=1000'],
  ]}
/>

In a single-tenant deployment without complete `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` environment credentials, an authorized administrator can configure one encrypted connection under **Settings → Langfuse**. Complete environment credentials take precedence and hide that setting. Fanout deployments use the same setting for the tenant connection when fanout and tenant export are enabled.

`LANGFUSE_BASE_URL` is canonical. `LANGFUSE_HOST` and `LANGFUSE_BASEURL` remain compatibility aliases and are consulted in that order only when the canonical setting is absent.

### OpenTelemetry Tracing

LibreChat can emit backend OpenTelemetry traces for general API, HTTP, MongoDB, Mongoose, Redis, and outbound request visibility. Redis command-level spans are opt-in so default traces stay high-level. Use Langfuse for GenAI-specific prompt/model observability.

<OptionTable
  options={[
    [
      'OTEL_TRACING_ENABLED',
      'boolean',
      'Enable backend OpenTelemetry tracing. Tracing remains disabled when OTEL_SDK_DISABLED=true.',
      '# OTEL_TRACING_ENABLED=false',
    ],
    [
      'OTEL_SERVICE_NAME',
      'string',
      'Service name reported to OpenTelemetry. Default: librechat.',
      '# OTEL_SERVICE_NAME=librechat',
    ],
    [
      'OTEL_SERVICE_VERSION',
      'string',
      'Service version reported to OpenTelemetry. Defaults to the package version when unset.',
      '# OTEL_SERVICE_VERSION=',
    ],
    [
      'OTEL_EXPORTER_OTLP_ENDPOINT',
      'string',
      'Base OTLP exporter endpoint.',
      '# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318',
    ],
    [
      'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT',
      'string',
      'Trace-specific OTLP endpoint. Overrides the base endpoint for traces when set.',
      '# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=',
    ],
    [
      'OTEL_EXPORTER_OTLP_HEADERS',
      'string',
      'Comma-separated OTLP exporter headers, such as authorization metadata.',
      '# OTEL_EXPORTER_OTLP_HEADERS=',
    ],
    ['OTEL_TRACES_EXPORTER', 'string', 'Trace exporter selection.', '# OTEL_TRACES_EXPORTER=otlp'],
    [
      'OTEL_TRACES_SAMPLER',
      'string',
      'OpenTelemetry trace sampler. Default example: parentbased_always_on.',
      '# OTEL_TRACES_SAMPLER=parentbased_always_on',
    ],
    ['OTEL_LOG_LEVEL', 'string', 'OpenTelemetry SDK log level.', '# OTEL_LOG_LEVEL=INFO'],
    [
      'OTEL_SDK_DISABLED',
      'boolean',
      'Disable the OpenTelemetry SDK even if tracing is enabled.',
      '# OTEL_SDK_DISABLED=false',
    ],
    [
      'OTEL_IOREDIS_TRACING_ENABLED',
      'boolean',
      'Enable Redis command-level spans. Disabled by default to keep backend traces high-level.',
      '# OTEL_IOREDIS_TRACING_ENABLED=false',
    ],
  ]}
/>

### Real User Monitoring (Browser)

LibreChat can publish browser Real User Monitoring (RUM) telemetry to HyperDX-compatible OTLP collectors. RUM is disabled by default.

<OptionTable
  options={[
    [
      'RUM_ENABLED',
      'boolean',
      'Enable browser Real User Monitoring. Default: false.',
      '# RUM_ENABLED=false',
    ],
    [
      'RUM_PROVIDER',
      'string',
      'Browser RUM provider. Currently supports `hyperdx`.',
      '# RUM_PROVIDER=hyperdx',
    ],
    [
      'RUM_URL',
      'string',
      'Public collector URL used by public-token mode.',
      '# RUM_URL=http://localhost:4318',
    ],
    [
      'RUM_SERVICE_NAME',
      'string',
      'Service name reported by the browser SDK. Default: librechat-web.',
      '# RUM_SERVICE_NAME=librechat-web',
    ],
    [
      'RUM_ENVIRONMENT',
      'string',
      'Environment label reported with browser telemetry.',
      '# RUM_ENVIRONMENT=development',
    ],
    [
      'RUM_AUTH_MODE',
      'string',
      'Authentication mode for browser telemetry. Use `publicToken` or `proxy`.',
      '# RUM_AUTH_MODE=publicToken',
    ],
    [
      'RUM_PUBLIC_TOKEN',
      'string',
      'Public browser token for public-token mode. Treat this as public and restrict ingestion at the collector.',
      '# RUM_PUBLIC_TOKEN=',
    ],
    [
      'RUM_PROXY_TARGET_URL',
      'string',
      'Collector base URL used by authenticated proxy mode. Required when `RUM_AUTH_MODE=proxy`.',
      '# RUM_PROXY_TARGET_URL=http://otel-collector:4318',
    ],
    [
      'RUM_PROXY_TIMEOUT_MS',
      'number',
      'Proxy request timeout in milliseconds. Default: 10000.',
      '# RUM_PROXY_TIMEOUT_MS=10000',
    ],
    [
      'RUM_TRACE_PROPAGATION_TARGETS',
      'string',
      'Comma-separated first-party HTTPS origins or URLs that should receive traceparent headers.',
      '# RUM_TRACE_PROPAGATION_TARGETS=https://api.example.com',
    ],
    [
      'RUM_DISABLE_REPLAY',
      'boolean',
      'Disable browser session replay. Default: true.',
      '# RUM_DISABLE_REPLAY=true',
    ],
    [
      'RUM_CONSOLE_CAPTURE',
      'boolean',
      'Capture browser console logs. May collect sensitive prompts, responses, or payloads.',
      '# RUM_CONSOLE_CAPTURE=false',
    ],
    [
      'RUM_ADVANCED_NETWORK_CAPTURE',
      'boolean',
      'Capture detailed network payloads. May collect sensitive prompts, responses, or payloads.',
      '# RUM_ADVANCED_NETWORK_CAPTURE=false',
    ],
    [
      'RUM_SAMPLE_RATE',
      'number',
      'Browser telemetry sample rate from 0 to 1. Default: 1.',
      '# RUM_SAMPLE_RATE=1',
    ],
  ]}
/>

In `publicToken` mode, the browser sends telemetry directly to `RUM_URL` with `RUM_PUBLIC_TOKEN`. In `proxy` mode, the browser sends telemetry through LibreChat; the backend validates the user session, strips app authentication headers, and forwards telemetry to `RUM_PROXY_TARGET_URL`. Invalid or expired sessions are dropped with a `204` response so browser telemetry failures do not surface normal API authentication errors. Proxy outcomes are counted in `rum_proxy_requests_total` with `endpoint` and `result` labels on the LibreChat API `/metrics` endpoint.

For sampled page loads, LibreChat emits one `page-load-diagnostics` action when First Contentful Paint is observed. It includes navigation timing attribution such as time to first byte, first-byte-to-FCP time, service-worker timing, navigation type, and a normalized route instead of raw conversation IDs. RUM also records early page lifecycle, service-worker, stale-asset recovery, and single-page-app route-change events. Diagnostics are best-effort and never delay startup or trigger recovery behavior; early events are discarded when the page is not sampled.

### Configuration Path - `librechat.yaml`

Specify an alternative location for the LibreChat configuration file.
You may specify an **absolute path**, a **relative path**, or a **URL**. The filename in the path is flexible and does not have to be `librechat.yaml`; any valid configuration file will work.

> **Note**: If you prefer LibreChat to search for the configuration file in the root directory (which is the default behavior), simply leave this option commented out.

<OptionTable
  options={[
    [
      'CONFIG_PATH',
      'string',
      'An alternative location for the LibreChat configuration file.',
      '# CONFIG_PATH=https://raw.githubusercontent.com/danny-avila/LibreChat/main/librechat.example.yaml',
    ],
  ]}
/>

### Deployment Skills

Deployment Skills are loaded read-only at startup from the filesystem and exposed to users who have the Skills capability enabled.

<OptionTable
  options={[
    [
      'DEPLOYMENT_SKILLS_DIR',
      'string',
      'Directory containing deployment-provided Skills. Defaults to `./skill` at the project root.',
      '# DEPLOYMENT_SKILLS_DIR=./skill',
    ],
  ]}
/>

Restart LibreChat after changing this directory or any files inside it. Deployment-provided Skills take precedence over persisted Skills with the same name.

### Agent Plugins (Experimental)

LibreChat can load experimental Agent Plugins from immediate child directories at startup. A plugin can bundle deployment Skills, MCP servers, and optional command hooks.

<OptionTable
  options={[
    [
      'DEPLOYMENT_PLUGINS_DIR',
      'string',
      'Directory containing Agent Plugin packages. Defaults to `./plugin` at the project root.',
      '# DEPLOYMENT_PLUGINS_DIR=./plugin',
    ],
    [
      'DEPLOYMENT_PLUGIN_DATA_DIR',
      'string',
      'Persistent data root for Agent Plugins. Defaults to `./data/plugins` at the project root.',
      '# DEPLOYMENT_PLUGIN_DATA_DIR=./data/plugins',
    ],
    [
      'DEPLOYMENT_PLUGIN_HOOKS',
      'boolean',
      'Runs trusted `command` handlers declared in `ai.librechat/hooks/hooks.json`. Disabled by default. Commands execute as child processes on the API host.',
      '# DEPLOYMENT_PLUGIN_HOOKS=true',
    ],
  ]}
/>

See [Agent Plugins](/docs/features/agent_plugins) for package layout, schema versions, supported components, precedence, and current limitations.

### Configuration Validation

By default, LibreChat will exit with an error (exit code 1) if the `librechat.yaml` configuration file contains validation errors. This fail-fast behavior helps catch configuration issues early in deployment pipelines and prevents running with unintended default settings.

<OptionTable
  options={[
    [
      'CONFIG_BYPASS_VALIDATION',
      'boolean',
      'When set to `true`, the server will log a warning and continue starting with default configuration even if `librechat.yaml` has validation errors. This preserves the legacy behavior.',
      '# CONFIG_BYPASS_VALIDATION=true',
    ],
  ]}
/>

<Callout type="warning" title="Warning">
  Using `CONFIG_BYPASS_VALIDATION=true` is not recommended for production environments. It is
  intended as a temporary workaround while debugging configuration issues. Always fix validation
  errors in your configuration file.
</Callout>

### Uncaught Exception Handling

By default, LibreChat will exit the process when an uncaught exception occurs, which is the standard Node.js behavior. You can override this to keep the app running after uncaught exceptions.

<OptionTable
  options={[
    [
      'CONTINUE_ON_UNCAUGHT_EXCEPTION',
      'boolean',
      'When set to `true`, the app will continue running after encountering uncaught exceptions instead of exiting the process.',
      '# CONTINUE_ON_UNCAUGHT_EXCEPTION=false',
    ],
  ]}
/>

<Callout type="warning" title="Warning">
  Not recommended for production unless necessary. Uncaught exceptions may leave the application in
  an unpredictable state.
</Callout>

## Endpoints

In this section, you can configure the endpoints and models selection, their API keys, and the proxy and reverse proxy settings for the endpoints that support it.

### General Config

Uncomment `ENDPOINTS` to customize the available endpoints in LibreChat.

<OptionTable
  options={[
    [
      'ENDPOINTS',
      'string',
      'Comma-separated list of available endpoints.',
      '# ENDPOINTS=openAI,agents,assistants,gptPlugins,azureOpenAI,google,anthropic,bingAI,custom',
    ],
    [
      'PROXY',
      'string',
      'Outbound proxy for supported server-side clients. Applies to both HTTP and HTTPS targets.',
      'PROXY=',
    ],
    [
      'HTTP_PROXY',
      'string',
      'HTTP proxy fallback used by supported server-side clients when PROXY is unset.',
      '# HTTP_PROXY=',
    ],
    [
      'HTTPS_PROXY',
      'string',
      'HTTPS proxy fallback used by supported server-side clients when PROXY is unset.',
      '# HTTPS_PROXY=',
    ],
    [
      'NO_PROXY',
      'string',
      'Comma-separated hosts, domains, or IP ranges that supported server-side clients should bypass. The lowercase no_proxy variant is also honored.',
      '# NO_PROXY=',
    ],
    ['TITLE_CONVO', 'boolean', 'Enable titling for all endpoints.', 'TITLE_CONVO=true'],
  ]}
/>

### Known Endpoints - `librechat.yaml`

- see also: [Custom Endpoints & Configuration](/docs/configuration/librechat_yaml)

<OptionTable
  options={[
    ['ANYSCALE_API_KEY', 'string', 'API key for Anyscale.', '# ANYSCALE_API_KEY='],
    ['APIPIE_API_KEY', 'string', 'API key for Apipie.', '# APIPIE_API_KEY='],
    ['COHERE_API_KEY', 'string', 'API key for Cohere.', '# COHERE_API_KEY='],
    ['FIREWORKS_API_KEY', 'string', 'API key for Fireworks.', '# FIREWORKS_API_KEY='],
    ['GROQ_API_KEY', 'string', 'API key for Groq.', '# GROQ_API_KEY='],
    ['MISTRAL_API_KEY', 'string', 'API key for Mistral.', '# MISTRAL_API_KEY='],
    ['OPENROUTER_KEY', 'string', 'API key for OpenRouter.', '# OPENROUTER_KEY='],
    ['PERPLEXITY_API_KEY', 'string', 'API key for Perplexity.', '# PERPLEXITY_API_KEY='],
    ['SHUTTLEAI_API_KEY', 'string', 'API key for ShuttleAI.', '# SHUTTLEAI_API_KEY='],
    ['TOGETHERAI_API_KEY', 'string', 'API key for TogetherAI.', '# TOGETHERAI_API_KEY='],
    ['DEEPSEEK_API_KEY', 'string', 'API key for Deepseek API', '# DEEPSEEK_API_KEY='],
  ]}
/>

### Web Search

The web search feature enables internet search capabilities within LibreChat.

**Important**: The exact environment variable names shown below are default references and can be customized through the `librechat.yaml` configuration file to use any variable names you prefer.

For detailed configuration and customization options, see: [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search)

<OptionTable
  options={[
    [
      'SERPER_API_KEY',
      'string',
      'API key for Serper search provider. Get your key from https://serper.dev/api-keys',
      '# SERPER_API_KEY=',
    ],
    [
      'TAVILY_API_KEY',
      'string',
      'API key for Tavily search and scraper provider. Get your key from https://app.tavily.com/home',
      '# TAVILY_API_KEY=',
    ],
    [
      'TAVILY_SEARCH_URL',
      'string',
      'Custom Tavily Search API URL (optional). Only needed for custom or proxy Tavily-compatible search endpoints.',
      '# TAVILY_SEARCH_URL=',
    ],
    [
      'TAVILY_EXTRACT_URL',
      'string',
      'Custom Tavily Extract API URL (optional). Only needed for custom or proxy Tavily-compatible extract endpoints.',
      '# TAVILY_EXTRACT_URL=',
    ],
    [
      'KEENABLE_API_KEY',
      'string',
      'Optional Keenable API key for search and page fetch. Public endpoints work keyless; a key raises their rate limits.',
      '# KEENABLE_API_KEY=',
    ],
    [
      'KEENABLE_API_URL',
      'string',
      'Optional custom Keenable search API URL.',
      '# KEENABLE_API_URL=',
    ],
    [
      'KEENABLE_FETCH_URL',
      'string',
      'Optional custom Keenable page-fetch API URL used when scraperProvider is keenable.',
      '# KEENABLE_FETCH_URL=',
    ],
    [
      'FIRECRAWL_API_KEY',
      'string',
      'API key for Firecrawl scraper service. Get your key from https://docs.firecrawl.dev/introduction#api-key',
      '# FIRECRAWL_API_KEY=',
    ],
    [
      'FIRECRAWL_API_URL',
      'string',
      'Custom Firecrawl API URL (optional). Only needed for custom Firecrawl instances.',
      '# FIRECRAWL_API_URL=',
    ],
    ['FIRECRAWL_VERSION', 'string', 'Firecrawl API version (v0 or v1).', '# FIRECRAWL_VERSION=v1'],
    [
      'JINA_API_KEY',
      'string',
      'API key for Jina reranker service. Get your key from https://jina.ai/api-dashboard/',
      '# JINA_API_KEY=',
    ],
    [
      'JINA_API_URL',
      'string',
      'Custom Jina API URL (optional). Only needed for custom Jina instances.',
      '# JINA_API_URL=',
    ],
    [
      'COHERE_API_KEY',
      'string',
      'API key for Cohere reranker service. Get your key from https://dashboard.cohere.com/welcome/login',
      '# COHERE_API_KEY=',
    ],
  ]}
/>

**Note**: Most variable names can be customized in your `librechat.yaml` configuration file. For example, you could use `CUSTOM_SERPER_KEY` instead of `SERPER_API_KEY` by configuring it in the web search settings. `KEENABLE_FETCH_URL` is environment-only. See the [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search) documentation for details.

### Anthropic

see: [Anthropic Endpoint](/docs/configuration/pre_configured_ai/anthropic)

- You can request an access key from https://platform.claude.com/
- Leave `ANTHROPIC_API_KEY=` blank to disable this endpoint
- Set `ANTHROPIC_API_KEY=` to "user_provided" to allow users to provide their own API key from the WebUI
- If you have access to a reverse proxy for `Anthropic`, you can set it with `ANTHROPIC_REVERSE_PROXY=`
  - leave blank or comment it out to use default base url

<OptionTable
  options={[
    [
      'ANTHROPIC_API_KEY',
      'string',
      'Anthropic API key or "user_provided" to allow users to provide their own API key.',
      'Defaults to an empty string.',
    ],
    [
      'ANTHROPIC_MODELS',
      'string',
      'Comma-separated list of Anthropic models to use.',
      '# ANTHROPIC_MODELS=claude-fable-5-1,claude-fable-5,claude-opus-5,claude-opus-4-8,claude-opus-4-7,claude-sonnet-5,claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022',
    ],
    [
      'ANTHROPIC_REVERSE_PROXY',
      'string',
      'Reverse proxy for Anthropic.',
      '# ANTHROPIC_REVERSE_PROXY=',
    ],
    [
      'ANTHROPIC_TITLE_MODEL',
      'string',
      'DEPRECATED: Model to use for titling with Anthropic.',
      '# ANTHROPIC_TITLE_MODEL=claude-3-haiku-20240307',
    ],
  ]}
/>

- `ANTHROPIC_TITLE_MODEL` is now deprecated and will be removed in future versions. Use the [`titleModel` Endpoint Setting](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#titlemodel) instead in the `librechat.yaml` config instead.

> **Note:** Must be compatible with the Anthropic Endpoint. Also, Claude 2 and Claude 3 models perform best at this task, with `claude-3-haiku` models being the cheapest.

Claude Fable 5.1 and Fable 5 are included in the default Anthropic model list. Fable/Mythos-class
models use the modern Anthropic behavior in LibreChat: 1M context, adaptive thinking
support, prompt caching support, and `thinkingDisplay` handling for summarized or
omitted reasoning output.

#### Anthropic via Vertex AI

You can also use Anthropic Claude models through Google Cloud Vertex AI. For detailed YAML configuration options, see: [Anthropic Vertex AI Configuration](/docs/configuration/librechat_yaml/object_structure/anthropic_vertex)

<OptionTable
  options={[
    [
      'ANTHROPIC_USE_VERTEX',
      'boolean',
      'Set to true to use Anthropic models through Google Vertex AI instead of direct API.',
      'ANTHROPIC_USE_VERTEX=true',
    ],
    [
      'ANTHROPIC_VERTEX_REGION',
      'string',
      'The Google Cloud location for Vertex AI. Default: us-east5. Use global, us, or eu for Opus 4.7+, Opus 5, Sonnet 5, and Fable/Mythos 5.',
      'ANTHROPIC_VERTEX_REGION=global',
    ],
  ]}
/>

> **Note:** When using Vertex AI, you must also configure `GOOGLE_SERVICE_KEY_FILE` (see [Google Configuration](#google)) with a service account that has the `Vertex AI User` role.

### AWS Bedrock

See: [AWS Bedrock Setup](/docs/configuration/pre_configured_ai/bedrock)

<OptionTable
  options={[
    [
      'BEDROCK_AWS_DEFAULT_REGION',
      'string',
      'A default AWS region must be provided for Bedrock.',
      'BEDROCK_AWS_DEFAULT_REGION=us-east-1',
    ],
    [
      'BEDROCK_AWS_ACCESS_KEY_ID',
      'string',
      'AWS access key ID for Bedrock. Optional if using default AWS credentials chain.',
      '# BEDROCK_AWS_ACCESS_KEY_ID=your_access_key_id',
    ],
    [
      'BEDROCK_AWS_SECRET_ACCESS_KEY',
      'string',
      'AWS secret access key for Bedrock. Optional if using default AWS credentials chain.',
      '# BEDROCK_AWS_SECRET_ACCESS_KEY=your_secret_access_key',
    ],
    [
      'BEDROCK_AWS_SESSION_TOKEN',
      'string',
      'AWS session token for temporary credentials. Optional.',
      '# BEDROCK_AWS_SESSION_TOKEN=your_session_token',
    ],
    [
      'BEDROCK_AWS_PROFILE',
      'string',
      'AWS shared config profile name for Bedrock. Optional if using the default AWS credentials chain.',
      '# BEDROCK_AWS_PROFILE=your-profile-name',
    ],
    [
      'BEDROCK_AWS_BEARER_TOKEN',
      'string',
      'Amazon Bedrock API key for bearer auth, or user_provided to let users enter their own Bedrock API key in the UI.',
      '# BEDROCK_AWS_BEARER_TOKEN=your_bedrock_api_key',
    ],
    [
      'BEDROCK_AWS_MODELS',
      'string',
      'Comma-separated list of Bedrock model IDs. If omitted, all known supported models are included.',
      '# BEDROCK_AWS_MODELS=global.anthropic.claude-fable-5-1,global.anthropic.claude-fable-5,global.anthropic.claude-opus-5,global.anthropic.claude-opus-4-8,global.anthropic.claude-opus-4-7,global.anthropic.claude-sonnet-5,global.anthropic.claude-sonnet-4-6,meta.llama3-1-8b-instruct-v1:0',
    ],
  ]}
/>

> **Note:** You can omit the access keys to use the default AWS credentials chain (environment variables, SSO credentials, shared credentials files, or EC2/ECS Instance Metadata Service). See [AWS Bedrock Setup](/docs/configuration/pre_configured_ai/bedrock) for more details.

Claude Fable/Mythos-class models on Bedrock are inference-profile only. Use a profile
ID such as `global.anthropic.claude-fable-5-1`, and enable the required Anthropic data
sharing setting in the Bedrock console or Data Retention API before invoking them.

### BingAI

Bing, also used for Sydney, jailbreak, and Bing Image Creator

<OptionTable
  options={[
    [
      'BINGAI_TOKEN',
      'string',
      'Bing access token. Leave blank to disable. Can be set to "user_provided" to allow users to provide their own token from the WebUI.',
      'BINGAI_TOKEN=user_provided',
    ],
    [
      'BINGAI_HOST',
      'string',
      'Bing host URL. Leave commented out to use default server.',
      '# BINGAI_HOST=https://cn.bing.com',
    ],
  ]}
/>

Note: It is recommended to leave it as "user_provided" and provide the token from the WebUI.

### Google

Follow these instructions to setup the [Google Endpoint](/docs/configuration/pre_configured_ai/google)

<OptionTable
  options={[
    [
      'GOOGLE_KEY',
      'string',
      'Google API key. Set to "user_provided" to allow users to provide their own API key from the WebUI.',
      'GOOGLE_KEY=user_provided',
    ],
    [
      'GOOGLE_SERVICE_KEY_FILE',
      'string',
      'Path to Google service account JSON key file, URL to fetch it from, or stringified JSON. Used for Vertex AI authentication (e.g., OCR features).',
      'GOOGLE_SERVICE_KEY_FILE=/path/to/auth.json',
    ],
    ['GOOGLE_REVERSE_PROXY', 'string', 'Google reverse proxy URL.', 'GOOGLE_REVERSE_PROXY='],
    [
      'GOOGLE_AUTH_HEADER',
      'boolean',
      'Use Authorization header instead of X-goog-api-key. Some reverse proxies require this.',
      '# GOOGLE_AUTH_HEADER=true',
    ],
    [
      'GOOGLE_MODELS',
      'string',
      'Available Gemini API Google models, separated by commas.',
      'GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite',
    ],
    [
      'GOOGLE_MODELS',
      'string',
      'Available Vertex AI Google models, separated by commas.',
      'GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001',
    ],
    [
      'GOOGLE_TITLE_MODEL',
      'string',
      'DEPRECATED: The model used for titling with Google.',
      'GOOGLE_TITLE_MODEL=gemini-pro',
    ],
    [
      'GOOGLE_LOC',
      'string',
      'Specifies the Google Cloud location for processing API requests',
      'GOOGLE_LOC=us-central1',
    ],
    [
      'GOOGLE_CLOUD_LOCATION',
      'string',
      'Alternative region for Gemini Image Generation (e.g., global).',
      '# GOOGLE_CLOUD_LOCATION=global',
    ],
    [
      'GOOGLE_EXCLUDE_SAFETY_SETTINGS',
      'string',
      'Completely omit the safety settings that are included by default, which will use provider defaults',
      'GOOGLE_EXCLUDE_SAFETY_SETTINGS=true',
    ],
    [
      'GOOGLE_SAFETY_SEXUALLY_EXPLICIT',
      'string',
      'Safety setting for sexually explicit content. Options are BLOCK_ALL, BLOCK_ONLY_HIGH, WARN_ONLY, and OFF.',
      'GOOGLE_SAFETY_SEXUALLY_EXPLICIT=BLOCK_ONLY_HIGH',
    ],
    [
      'GOOGLE_SAFETY_HATE_SPEECH',
      'string',
      'Safety setting for hate speech content. Options are BLOCK_ALL, BLOCK_ONLY_HIGH, WARN_ONLY, and OFF.',
      'GOOGLE_SAFETY_HATE_SPEECH=BLOCK_ONLY_HIGH',
    ],
    [
      'GOOGLE_SAFETY_HARASSMENT',
      'string',
      'Safety setting for harassment content. Options are BLOCK_ALL, BLOCK_ONLY_HIGH, WARN_ONLY, and OFF.',
      'GOOGLE_SAFETY_HARASSMENT=BLOCK_ONLY_HIGH',
    ],
    [
      'GOOGLE_SAFETY_DANGEROUS_CONTENT',
      'string',
      'Safety setting for dangerous content. Options are BLOCK_ALL, BLOCK_ONLY_HIGH, WARN_ONLY, and OFF.',
      'GOOGLE_SAFETY_DANGEROUS_CONTENT=BLOCK_ONLY_HIGH',
    ],
    [
      'GOOGLE_SAFETY_CIVIC_INTEGRITY',
      'string',
      'Safety setting for civic integrity content. Options are BLOCK_ALL, BLOCK_ONLY_HIGH, WARN_ONLY, and OFF.',
      '# GOOGLE_SAFETY_CIVIC_INTEGRITY=BLOCK_ONLY_HIGH',
    ],
  ]}
/>

Customize the available models, separated by commas, **without spaces**. The first will be default. Leave it blank or commented out to use internal settings.

- `GOOGLE_TITLE_MODEL` is now deprecated and will be removed in future versions. Use the [`titleModel` Endpoint Setting](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#titlemodel) instead in the `librechat.yaml` config instead.

**Note:** For the Vertex AI `GOOGLE_SAFETY` variables, you do not have access to the `BLOCK_NONE` setting by default. To use this restricted `HarmBlockThreshold` setting, you will need to either:

- (a) Get access through an allowlist via your Google account team
- (b) Switch your account type to monthly invoiced billing following this instruction:
  https://cloud.google.com/billing/docs/how-to/invoiced-billing

#### Gemini Image Generation

Gemini Image Generation is a tool for Agents that supports both the Gemini API and Vertex AI. See: [Gemini Image Generation](/docs/configuration/tools/gemini_image_gen)

<OptionTable
  options={[
    [
      'GEMINI_API_KEY',
      'string',
      'Dedicated Gemini API key for image generation. Falls back to GOOGLE_KEY if not set.',
      '# GEMINI_API_KEY=your_gemini_api_key',
    ],
    [
      'GEMINI_IMAGE_MODEL',
      'string',
      'Gemini model for image generation. Default: gemini-2.5-flash-image.',
      '# GEMINI_IMAGE_MODEL=gemini-2.5-flash-image',
    ],
  ]}
/>

> **Note:** When no API key is configured, the tool automatically falls back to Vertex AI using the service account from `GOOGLE_SERVICE_KEY_FILE`. The service account must have the `Vertex AI User` role.

### OpenAI

See: [OpenAI Setup](/docs/configuration/pre_configured_ai/openai)

<OptionTable
  options={[
    [
      'OPENAI_API_KEY',
      'string',
      'Your OpenAI API key. Leave blank to disable this endpoint or set to "user_provided" to allow users to provide their own API key from the WebUI.',
      'OPENAI_API_KEY=user_provided',
    ],
    [
      'OPENAI_MODELS',
      'string',
      'Customize the available models, separated by commas, without spaces. The first will be default. Leave commented out to use internal settings.',
      '# OPENAI_MODELS=gpt-5,gpt-5-codex,gpt-5-mini,gpt-5-nano,o3-pro,o3,o4-mini,gpt-4.1,gpt-4.1-mini,gpt-4.1-nano,o3-mini,o1-pro,o1,gpt-4o,gpt-4o-mini',
    ],
    ['DEBUG_OPENAI', 'boolean', 'Enable debug mode for the OpenAI endpoint.', 'DEBUG_OPENAI=false'],
    [
      'OPENAI_SUMMARIZE',
      'boolean',
      'Enable message summarization. False by default',
      '# OPENAI_SUMMARIZE=true',
    ],
    [
      'OPENAI_SUMMARY_MODEL',
      'string',
      'The model used for OpenAI summarization.',
      '# OPENAI_SUMMARY_MODEL=gpt-3.5-turbo',
    ],
    [
      'OPENAI_FORCE_PROMPT',
      'boolean',
      'Force the API to be called with a prompt payload instead of a messages payload.',
      '# OPENAI_FORCE_PROMPT=false',
    ],
    [
      'OPENAI_ORGANIZATION',
      'string',
      'Specify which organization to use for each API request to OpenAI. Optional',
      '# OPENAI_ORGANIZATION=',
    ],
    [
      'OPENAI_REVERSE_PROXY',
      'string',
      'DEPRECATED: Reverse proxy settings for OpenAI.',
      '# OPENAI_REVERSE_PROXY=',
    ],
    [
      'OPENAI_TITLE_MODEL',
      'string',
      'DEPRECATED: The model used for OpenAI titling.',
      '# OPENAI_TITLE_MODEL=gpt-3.5-turbo',
    ],
  ]}
/>

- `OPENAI_TITLE_MODEL` is now deprecated and will be removed in future versions. Use the [`titleModel` Endpoint Setting](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#titlemodel) instead in the `librechat.yaml` config instead.
- `OPENAI_REVERSE_PROXY` is now deprecated and will be removed in future versions. Use a [custom endpoint](/docs/quick_start/custom_endpoints) instead.

### Assistants

See: [Assistants Setup](/docs/configuration/pre_configured_ai/assistants)

<OptionTable
  options={[
    [
      'ASSISTANTS_API_KEY',
      'string',
      'Your OpenAI API key for Assistants API. Leave blank to disable this endpoint or set to "user_provided" to allow users to provide their own API key from the WebUI.',
      'ASSISTANTS_API_KEY=user_provided',
    ],
    [
      'ASSISTANTS_MODELS',
      'string',
      'Customize the available models, separated by commas, without spaces. The first will be default. Leave blank to use internal settings.',
      '# ASSISTANTS_MODELS=gpt-3.5-turbo-0125,gpt-3.5-turbo-16k-0613,gpt-3.5-turbo-16k,gpt-3.5-turbo,gpt-4,gpt-4-0314,gpt-4-32k-0314,gpt-4-0613,gpt-3.5-turbo-0613,gpt-3.5-turbo-1106,gpt-4-0125-preview,gpt-4-turbo-preview,gpt-4-1106-preview',
    ],
    [
      'ASSISTANTS_BASE_URL',
      'string',
      'Alternate base URL for Assistants API.',
      '# ASSISTANTS_BASE_URL=',
    ],
  ]}
/>

Note: You can customize the available models, separated by commas, without spaces. The first will be default. Leave it blank or commented out to use internal settings.

### Tavily

Get your API key here: **[https://tavily.com/#api](https://tavily.com/#api)**

**Environment Variables:**

<OptionTable options={[['TAVILY_API_KEY', 'string', 'Tavily API key.', 'TAVILY_API_KEY=']]} />

### Traversaal

**Description:** LLM-enhanced search tool.

Get API key here: **https://api.traversaal.ai/dashboard**

**Environment Variables:**

<OptionTable
  options={[['TRAVERSAAL_API_KEY', 'string', 'Traversaal API key.', 'TRAVERSAAL_API_KEY=']]}
/>

### WolframAlpha

See detailed instructions here: **[Wolfram Alpha](/docs/configuration/tools/wolfram)**

**Environment Variables:**

<OptionTable options={[['WOLFRAM_APP_ID', 'string', 'Wolfram Alpha App ID.', 'WOLFRAM_APP_ID=']]} />

### Zapier

**Description:** - You need a Zapier account. Get your API key from here: **[Zapier](https://nla.zapier.com/credentials/)**

- Create allowed actions - Follow step 3 in this getting start guide from Zapier

**Note:** Zapier is known to be finicky with certain actions. Writing email drafts is probably the best use of it.

**Environment Variables:**

<OptionTable
  options={[['ZAPIER_NLA_API_KEY', 'string', 'Zapier NLA API key.', 'ZAPIER_NLA_API_KEY=']]}
/>

### OpenWeather

See detailed instructions here: **[OpenWeather](/docs/configuration/tools/openweather)**

<OptionTable
  options={[
    [
      'OPENWEATHER_API_KEY',
      'string',
      'OpenWeather API key for the One Call API 3.0.',
      'OPENWEATHER_API_KEY=',
    ],
  ]}
/>

## File Uploads

<OptionTable
  options={[
    [
      'FILE_UPLOAD_SSE_ENABLED',
      'boolean',
      'Stream upload responses with heartbeat events during long-running file processing. Default: false.',
      '# FILE_UPLOAD_SSE_ENABLED=false',
    ],
    [
      'REMOTE_FILE_FETCH_TIMEOUT_MS',
      'number',
      'Timeout in milliseconds for server-side remote file downloads. Default: 15000.',
      '# REMOTE_FILE_FETCH_TIMEOUT_MS=15000',
    ],
    [
      'REMOTE_FILE_FETCH_MAX_BYTES',
      'number',
      'Maximum size in bytes for server-side remote file downloads. Default: 536870912 (512 MiB).',
      '# REMOTE_FILE_FETCH_MAX_BYTES=536870912',
    ],
  ]}
/>

With `FILE_UPLOAD_SSE_ENABLED=true`, clients that request `text/event-stream` receive one-second heartbeat events while upload processing is still running, followed by a `data` or `error` event and a final `close` event. Clients that do not request SSE continue to receive the normal JSON response. This is useful when a reverse proxy would otherwise close an idle connection during long OCR, parsing, or RAG work.

The remote fetch controls apply when LibreChat ingests an HTTP or HTTPS file URL into local, Firebase, Azure, or S3/CloudFront storage. LibreChat checks both a declared `Content-Length` and the bytes actually streamed, so the limit still applies when the remote server omits or misstates its length. These controls are independent of the per-upload limits in [`fileConfig`](/docs/configuration/librechat_yaml/object_structure/file_config).

## Code Interpreter

The Code Interpreter API provides a secure environment for executing code and managing files. See: [Code Interpreter API](/docs/features/code_interpreter)

<OptionTable
  options={[
    [
      'LIBRECHAT_CODE_API_KEY',
      'string',
      'API key for the Code Interpreter service. When set globally, provides access to all users.',
      'LIBRECHAT_CODE_API_KEY=your-api-key',
    ],
    [
      'LIBRECHAT_CODE_BASEURL',
      'string',
      'Base URL for the normal stateless Code Interpreter service.',
      '# LIBRECHAT_CODE_BASEURL=https://your-custom-domain.com',
    ],
    [
      'LIBRECHAT_CODE_BASEURL_STATEFUL',
      'string',
      'Default Code Interpreter base URL for highly experimental stateful Agent sessions when no named statefulCodeSessions environment is selected. The service must advertise the stateful profile.',
      '# LIBRECHAT_CODE_BASEURL_STATEFUL=https://your-stateful-code-domain.com',
    ],
    [
      'CODE_SANDBOX_PREWARM',
      'boolean',
      'Prewarm selected stateful sandboxes in parallel with model generation. Set to false to disable. Default: true.',
      '# CODE_SANDBOX_PREWARM=true',
    ],
    [
      'CODE_SANDBOX_COLD_AFTER_MS',
      'number',
      'Time in milliseconds before LibreChat treats a tracked sandbox as cold. Default: 2100000 (35 minutes).',
      '# CODE_SANDBOX_COLD_AFTER_MS=2100000',
    ],
    [
      'LIBRECHAT_CODE_SANDBOX_OUTPUT_MAX_SIZE',
      'number',
      "Sandbox stdout budget in bytes used to derive image-read window sizes. Match the runner's SANDBOX_OUTPUT_MAX_SIZE to minimize execution round trips. Default: 65536.",
      '# LIBRECHAT_CODE_SANDBOX_OUTPUT_MAX_SIZE=65536',
    ],
    [
      'LIBRECHAT_CODE_IMAGE_CHUNK_BYTES',
      'number',
      'Exact bytes read per sandbox image window. Overrides the size derived from LIBRECHAT_CODE_SANDBOX_OUTPUT_MAX_SIZE; leave unset unless an exact override is required.',
      '# LIBRECHAT_CODE_IMAGE_CHUNK_BYTES=',
    ],
    [
      'CODE_ENVIRONMENT_PAIRING_USER_MAX',
      'number',
      'Maximum self-service code-worker pairing requests per user during one rate-limit window. Default: 5.',
      '# CODE_ENVIRONMENT_PAIRING_USER_MAX=5',
    ],
    [
      'CODE_ENVIRONMENT_PAIRING_USER_WINDOW',
      'number',
      'Self-service code-worker pairing rate-limit window in minutes. Default: 60.',
      '# CODE_ENVIRONMENT_PAIRING_USER_WINDOW=60',
    ],
  ]}
/>

If a worker has a smaller stdout cap than configured, LibreChat narrows and remembers the image window for that Code Interpreter base URL after a failed read. Matching the two budgets avoids that discarded discovery request and reduces pressure on the Code Interpreter execution rate limit.

### Code Interpreter JWT Authentication

The current self-hosted [ClickHouse/code-interpreter](https://github.com/ClickHouse/code-interpreter) service verifies short-lived LibreChat bearer tokens outside local mode. Set `CODEAPI_AUTH_PROVIDER=librechat-jwt` to enable token minting, then give Code Interpreter the public key that matches LibreChat's private signer. See [Self-hosted JWT authentication](/docs/features/code_interpreter#self-hosted-jwt-authentication) for the paired service configuration and key-generation helper.

<OptionTable
  options={[
    [
      'CODEAPI_AUTH_PROVIDER',
      'string',
      'Set to librechat-jwt to mint per-user Code Interpreter bearer tokens. The compatibility value both also enables minting, but ClickHouse/code-interpreter expects librechat-jwt.',
      '# CODEAPI_AUTH_PROVIDER=librechat-jwt',
    ],
    [
      'CODEAPI_JWT_ENABLED',
      'boolean',
      'Explicitly enables Code Interpreter JWT minting when CODEAPI_AUTH_PROVIDER is not librechat-jwt or both. Default: false.',
      '# CODEAPI_JWT_ENABLED=true',
    ],
    [
      'CODEAPI_JWT_PRIVATE_KEY',
      'string',
      'Ed25519 or RSA private signing key in PEM format. Escaped \\n sequences are converted to newlines.',
      '# CODEAPI_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\\n..."',
    ],
    [
      'CODEAPI_JWT_PRIVATE_KEY_BASE64',
      'string',
      'Base64-encoded PEM private key. Used when CODEAPI_JWT_PRIVATE_KEY is unset.',
      '# CODEAPI_JWT_PRIVATE_KEY_BASE64=',
    ],
    [
      'CODEAPI_JWT_PRIVATE_JWK_JSON',
      'string',
      'Private signing key as JWK JSON. Used when both PEM settings are unset.',
      '# CODEAPI_JWT_PRIVATE_JWK_JSON={"kty":"OKP",...}',
    ],
    [
      'CODEAPI_JWT_ALGORITHM',
      'string',
      'Signing algorithm: EdDSA or RS256. Default: EdDSA.',
      '# CODEAPI_JWT_ALGORITHM=EdDSA',
    ],
    [
      'CODEAPI_JWT_KID',
      'string',
      'Signing-key ID included in the token header. Must identify the matching Code Interpreter verifier key. Default: lc-codeapi-2026-05.',
      '# CODEAPI_JWT_KID=lc-codeapi-2026-05',
    ],
    [
      'CODEAPI_JWT_ISSUER',
      'string',
      'Token issuer; must match Code Interpreter. Default: librechat.',
      '# CODEAPI_JWT_ISSUER=librechat',
    ],
    [
      'CODEAPI_JWT_AUDIENCE',
      'string',
      'Token audience; must match Code Interpreter. Default: codeapi.',
      '# CODEAPI_JWT_AUDIENCE=codeapi',
    ],
    [
      'CODEAPI_JWT_TTL_SECONDS',
      'number',
      'Bearer-token lifetime in seconds. Invalid or larger values resolve to the 300-second default and maximum.',
      '# CODEAPI_JWT_TTL_SECONDS=300',
    ],
    [
      'CODEAPI_JWT_MINT_CACHE_SECONDS',
      'number',
      'How long LibreChat can reuse a token for the same authenticated context. Default and maximum: 30 seconds.',
      '# CODEAPI_JWT_MINT_CACHE_SECONDS=30',
    ],
    [
      'CODEAPI_JWT_SINGLE_TENANT_ID',
      'string',
      'Fallback tenant ID when strict tenant isolation is disabled and no tenant context is available. Must match Code Interpreter. Default: legacy.',
      '# CODEAPI_JWT_SINGLE_TENANT_ID=legacy',
    ],
  ]}
/>

At least one private-key source is required when JWT minting is enabled. If more than one is set, LibreChat checks `CODEAPI_JWT_PRIVATE_KEY`, then `CODEAPI_JWT_PRIVATE_KEY_BASE64`, then `CODEAPI_JWT_PRIVATE_JWK_JSON`. `CODEAPI_JWT_KEY_ID` remains a compatibility alias for `CODEAPI_JWT_KID`.

For a multi-tenant deployment, enable `TENANT_ISOLATION_STRICT=true` in LibreChat and `CODEAPI_TENANT_ISOLATION_STRICT=true` in Code Interpreter. Requests without authenticated tenant context then fail instead of using `CODEAPI_JWT_SINGLE_TENANT_ID`.

### Stateful Code Interpreter Endpoint

[`stateful_code_sessions`](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) is highly experimental and requires a separate Code Interpreter route. Configure the default route with `LIBRECHAT_CODE_BASEURL_STATEFUL`, or define named managed or attached backends under [`endpoints.agents.statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions). The selected service must run the `stateful` profile; LibreChat sends `X-CodeAPI-Expected-Profile: stateful` with stateful requests.

Stateful requests never fall back to `LIBRECHAT_CODE_BASEURL`. If the selected named environment is unavailable, or neither a named default nor `LIBRECHAT_CODE_BASEURL_STATEFUL` is configured, LibreChat stops the Agent run with a configuration error. Stateless agents continue using `LIBRECHAT_CODE_BASEURL`, and the two execution profiles do not share a live workspace.

Each Agent can scope its stateful workspace to the signed-in user, the user and Agent together, or the user and conversation. See [Stateful Code Sessions](/docs/features/code_interpreter#stateful-code-sessions) for setup and persistence limitations.

Named attached environments can also expose a self-service pairing control plane with `pairing.allowPrincipalWorkers: true`. Authorized users manage those owner-bound workers under **Settings > Code environments**; pairing-only entries do not replace `LIBRECHAT_CODE_BASEURL_STATEFUL` and cannot serve as the deployment default.

## Artifacts

Artifacts leverage the CodeSandbox library for secure rendering of HTML/JS code. By default, the public CDN hosted by CodeSandbox is used.

Fortunately, for those with internal network requirements, you can [self-host the bundler](https://sandpack.codesandbox.io/docs/guides/hosting-the-bundler) that compiles the frontend code and specify a custom bundler URL for Sandpack.

For more info, including pre-made container images for self-hosting with metric requests removed, see: https://github.com/LibreChat-AI/codesandbox-client

<OptionTable
  options={[
    [
      'SANDPACK_BUNDLER_URL',
      'string',
      'Specifies a custom bundler URL for Sandpack, used by Artifacts',
      'SANDPACK_BUNDLER_URL=your-bundler-url',
    ],
  ]}
/>

## Search (Meilisearch)

Search is disabled by default. Enable it only after configuring Meilisearch with a unique master key:

<OptionTable
  options={[['SEARCH', 'boolean', 'Enables search in messages and conversations. Default: false.', 'SEARCH=true']]}
/>

> Note: If you're not using docker, it requires the installation of the free self-hosted Meilisearch or a paid remote plan

To disable anonymized telemetry analytics for MeiliSearch for absolute privacy, set to true:

<OptionTable
  options={[
    [
      'MEILI_NO_ANALYTICS',
      'boolean',
      'Disables anonymized telemetry analytics for MeiliSearch.',
      'MEILI_NO_ANALYTICS=true',
    ],
  ]}
/>

For the API server to connect to the search server. Replace '0.0.0.0' with 'meilisearch' if serving MeiliSearch with docker-compose.

<OptionTable
  options={[
    [
      'MEILI_HOST',
      'string',
      'The API server connection to the search server.',
      'MEILI_HOST=http://0.0.0.0:7700',
    ],
  ]}
/>

This master key must be at least 16 bytes of valid UTF-8. Generate a unique value and give the same key to LibreChat and the Meilisearch service. Search requires both `SEARCH=true` and a configured `MEILI_MASTER_KEY`.

<OptionTable
  options={[
    [
      'MEILI_MASTER_KEY',
      'string',
      'The master key for MeiliSearch.',
      'MEILI_MASTER_KEY=',
    ],
  ]}
/>

To prevent LibreChat from attempting a database indexing sync with Meilisearch, you can set the following environment variable to `true`. This is useful in a node cluster, or multi-node setup, where only one instance should be responsible for indexing.

<OptionTable
  options={[
    [
      'MEILI_NO_SYNC',
      'string',
      'Toggle for disabling Mellisearch index sync',
      'MEILI_NO_SYNC=true',
    ],
  ]}
/>

## RAG API

Configure Retrieval-Augmented Generation for document indexing and context-aware responses. See: **[RAG API Configuration](/docs/configuration/rag_api)**

<OptionTable
  options={[
    [
      'RAG_API_URL',
      'string',
      'URL of the RAG API service.',
      'RAG_API_URL=http://host.docker.internal:8000',
    ],
    [
      'RAG_OPENAI_API_KEY',
      'string',
      'OpenAI API key for RAG embeddings. Overrides OPENAI_API_KEY for RAG.',
      '# RAG_OPENAI_API_KEY=sk-your-openai-api-key',
    ],
    [
      'RAG_OPENAI_BASEURL',
      'string',
      'Custom OpenAI base URL for RAG embeddings.',
      '# RAG_OPENAI_BASEURL=',
    ],
    [
      'RAG_USE_FULL_CONTEXT',
      'boolean',
      'Fetch entire file context instead of top 4 results. Default: false.',
      '# RAG_USE_FULL_CONTEXT=true',
    ],
    [
      'EMBEDDINGS_PROVIDER',
      'string',
      'Embeddings provider: openai, azure, huggingface, huggingfacetei, or ollama. Default: openai.',
      '# EMBEDDINGS_PROVIDER=openai',
    ],
    [
      'EMBEDDINGS_MODEL',
      'string',
      'Embeddings model to use. Default depends on provider.',
      '# EMBEDDINGS_MODEL=text-embedding-3-small',
    ],
  ]}
/>

> **Note:** When using the default Docker setup, the `.env` file is shared between LibreChat and the RAG API. For complete configuration options, see the [RAG API documentation](/docs/configuration/rag_api).

## Speech to Text & Text to Speech

Configure Speech-to-Text (STT) and Text-to-Speech (TTS) services. See: **[Speech Settings](/docs/configuration/stt_tts)**

<OptionTable
  options={[
    [
      'STT_API_KEY',
      'string',
      'API key for Speech-to-Text service (e.g., OpenAI Whisper).',
      '# STT_API_KEY=',
    ],
    [
      'TTS_API_KEY',
      'string',
      'API key for Text-to-Speech service (e.g., OpenAI TTS).',
      '# TTS_API_KEY=',
    ],
  ]}
/>

> **Note:** STT and TTS are primarily configured through the `speech:` section in `librechat.yaml`. These environment variables are referenced in that configuration. See [Speech Settings](/docs/configuration/stt_tts) for full YAML configuration options.

## Shared Links

Configure shared conversation links functionality.

<OptionTable
  options={[
    [
      'ALLOW_SHARED_LINKS',
      'boolean',
      'Enable or disable shared conversation links. Default: true.',
      'ALLOW_SHARED_LINKS=true',
    ],
    [
      'ALLOW_SHARED_LINKS_PUBLIC',
      'boolean',
      'Allow shared links to be publicly accessible without authentication. Default: false.',
      'ALLOW_SHARED_LINKS_PUBLIC=false',
    ],
    [
      'SHARED_LINKS_SNAPSHOT_FILES',
      'boolean',
      'Snapshot files referenced by a shared chat so viewers can preview or download them through the shared link. Overrides interface.sharedLinks.snapshotFiles when set.',
      'SHARED_LINKS_SNAPSHOT_FILES=true',
    ],
  ]}
/>

`ALLOW_SHARED_LINKS` is the feature-wide switch. Role permissions now control who can create shared links, share them with authenticated users, or make them visible to everyone; see [`interface.sharedLinks`](/docs/configuration/librechat_yaml/object_structure/interface#sharedlinks). `ALLOW_SHARED_LINKS_PUBLIC` only controls whether publicly shared links can be viewed without authentication. `SHARED_LINKS_SNAPSHOT_FILES` is a global override for shared-link file snapshots and can disable snapshot serving for every link when set to `false`.

## Scheduled Chats

<OptionTable
  options={[
    [
      'SCHEDULES_SINGLE_PROCESS',
      'boolean',
      'Allows Scheduled Chats without Redis only when exactly one LibreChat process is running. Never enable this for multiple processes or replicas.',
      'SCHEDULES_SINGLE_PROCESS=true',
    ],
    [
      'SCHEDULES_DISABLED',
      'boolean',
      'Emergency global stop for automatic Scheduled Chat occurrences and Run now. Definitions remain stored. Default: false.',
      'SCHEDULES_DISABLED=true',
    ],
  ]}
/>

Scheduled Chats are experimental and disabled until [`interface.schedules`](/docs/configuration/librechat_yaml/object_structure/interface#schedules) is configured. Multi-replica deployments require `USE_REDIS_STREAMS=true`; a deployment without shared Redis streams fails schedule writes closed unless it explicitly declares a truly single-process topology with `SCHEDULES_SINGLE_PROCESS=true`. A scheduled Agent that can pause for Ask User or tool approval always requires `USE_REDIS_STREAMS=true` plus a durable shared checkpointer; the built-in default is MongoDB. See [Scheduled Chats](/docs/features/scheduled_chats) for setup and runtime behavior.

## Agent Event Runtime

<OptionTable
  options={[
    [
      'AGENT_TRIGGERS_SELF_URL',
      'string',
      'Compatibility fallback for endpoints.agents.eventDriven.selfUrl. Base URL used by the Agent event host to re-enter fire, continue, and steer admission.',
      '# AGENT_TRIGGERS_SELF_URL=http://127.0.0.1:3080',
    ],
    [
      'AGENT_EVENT_USER_MAX',
      'number',
      'Compatibility fallback for rateLimits.agentEvents.userMax. Maximum Agent Event requests per API-key principal in the configured window. Default: 40.',
      'AGENT_EVENT_USER_MAX=40',
    ],
    [
      'AGENT_EVENT_USER_WINDOW',
      'number',
      'Compatibility fallback for rateLimits.agentEvents.userWindowInMinutes. Agent Event rate-limit window in minutes. Default: 1.',
      'AGENT_EVENT_USER_WINDOW=1',
    ],
  ]}
/>

Prefer [`endpoints.agents.eventDriven.selfUrl`](/docs/configuration/librechat_yaml/object_structure/agents#eventdriven) and [`rateLimits.agentEvents`](/docs/configuration/librechat_yaml/object_structure/config#ratelimits) in `librechat.yaml`. `AGENT_TRIGGERS_SELF_URL` and the rate-limit variables remain compatibility fallbacks; an explicitly configured YAML value takes precedence.

Bound child continuations, detached Subagent parent continuations, and Event Actor detached Action completion are automatic. The retired `ENABLE_AGENT_EVENT_CHILD_TURNS`, `ENABLE_SUBAGENT_COMPLETION_WAKEUPS`, and `AGENT_TRIGGERS_DETACHED_ACTIONS_PRODUCER_ENABLED` variables are no longer read.

The in-memory generation store supports process-local Event Actor detached completion while the process remains alive. Redis generation streams add durable restart recovery and replica handoff. See [Generation Protocol Compatibility](/docs/configuration/redis#generation-protocol-compatibility) before a mixed-version deployment.

Leave `AGENT_TRIGGERS_SELF_URL` unset for the normal bound-listener path. Set it only when internal event admission must traverse another HTTP origin, such as a TLS front door. The value must be an HTTP or HTTPS URL without embedded credentials.

LibreChat exposes an authenticated Agent Events API; it does not expose an unauthenticated webhook. API-key identity, Remote Agents permissions, target access, idempotency, and the dedicated Agent Event rate limit protect ingress. MongoDB-backed ordering lanes, actor mailboxes, receipts, leases, retries, and dead letters support workers across replicas. See [Agent Event Delivery](/docs/features/agents#agent-event-delivery) and [Agents API - Agent Events](/docs/features/agents_api#agent-events) for behavior and examples.

## User System

This section contains the configuration for:

- [Automated Moderation](#moderation)
- [Balance/Token Usage](#balance)
- [Registration and Social Logins](#registration-and-login)
- [Email Password Reset](#password-reset)

### Admin Insights

<OptionTable
  options={[
    [
      'ENABLE_INSIGHTS',
      'boolean',
      'Enables the MongoDB-backed Insights dashboard for administrators with the required capabilities. Default: false.',
      'ENABLE_INSIGHTS=false',
    ],
  ]}
/>

When enabled, Insights remains restricted to accounts with the `ADMIN` role plus `access:admin` and `read:insights`. Queries are tenant-scoped and expose persisted activity metrics and recent conversation metadata. See [Admin Insights](/docs/features/insights) for metric definitions and access details.

### Agent Conversation Controls

<OptionTable
  options={[
    [
      'STEER_MAX_LENGTH',
      'number',
      'Maximum characters allowed in one mid-run Agent steering message. Default: 16000.',
      '# STEER_MAX_LENGTH=16000',
    ],
  ]}
/>

### Moderation

The Automated Moderation System uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.

see: **[Automated Moderation](/docs/configuration/mod_system)**

#### Basic Moderation Settings

<OptionTable
  options={[
    [
      'OPENAI_MODERATION',
      'boolean',
      'Whether or not to enable OpenAI moderation on the **OpenAI** and **Plugins** endpoints.',
      'OPENAI_MODERATION=false',
    ],
    ['OPENAI_MODERATION_API_KEY', 'string', 'Your OpenAI API key.', 'OPENAI_MODERATION_API_KEY='],
    [
      'OPENAI_MODERATION_REVERSE_PROXY',
      'string',
      'Note: Commented out by default, this is not working with all reverse proxys.',
      '# OPENAI_MODERATION_REVERSE_PROXY=',
    ],
  ]}
/>

#### Banning Settings

<OptionTable
  options={[
    [
      'BAN_VIOLATIONS',
      'boolean',
      'Whether or not to enable banning users for violations (they will still be logged).',
      'BAN_VIOLATIONS=true',
    ],
    [
      'BAN_DURATION',
      'integer',
      'How long the user and associated IP are banned for (in milliseconds).',
      'BAN_DURATION=1000 * 60 * 60 * 2',
    ],
    [
      'BAN_INTERVAL',
      'integer',
      'The user will be banned every time their score reaches/crosses over the interval threshold.',
      'BAN_INTERVAL=20',
    ],
    [
      'VIOLATION_SCORE_TTL',
      'integer',
      'How long a violation score lives without new violations (in milliseconds). Each new violation restarts the countdown, so scores decay after a quiet period instead of accumulating forever. Set to 0 to never expire scores (legacy behavior).',
      'VIOLATION_SCORE_TTL=1000 * 60 * 60',
    ],
  ]}
/>

#### Login and registration rate limiting

Prevents brute force attacks and spam registrations by limiting login attempts and new account registrations.

<OptionTable
  options={[
    [
      'LOGIN_MAX',
      'integer',
      'The max amount of logins allowed per IP per LOGIN_WINDOW.',
      'LOGIN_MAX=7',
    ],
    [
      'LOGIN_WINDOW',
      'integer',
      'In minutes, determines the window of time for LOGIN_MAX logins.',
      'LOGIN_WINDOW=5',
    ],
    [
      'REGISTER_MAX',
      'integer',
      'The max amount of registrations allowed per IP per REGISTER_WINDOW.',
      'REGISTER_MAX=5',
    ],
    [
      'REGISTER_WINDOW',
      'integer',
      'In minutes, determines the window of time for REGISTER_MAX registrations.',
      'REGISTER_WINDOW=60',
    ],
  ]}
/>

The login-attempt budget is shared by the local login API and top-level social or federated OAuth navigations from the same IP. A rejected API login receives the existing JSON `429` response. A rate-limited or banned `/oauth/*` browser navigation returns to `/login?redirect=false` with a localized error code instead of rendering a JSON document; `redirect=false` prevents an automatic OpenID redirect from immediately entering the limiter again.

#### Password reset and email verification rate limiting

LibreChat applies separate IP-based limits to requesting an email and submitting the token from that email. This prevents repeated token guesses without forcing deployments to use the same limit for email delivery and token validation.

<OptionTable
  options={[
    [
      'RESET_PASSWORD_MAX',
      'integer',
      'Maximum password-reset email requests per IP in RESET_PASSWORD_WINDOW. Default: 2.',
      '# RESET_PASSWORD_MAX=2',
    ],
    [
      'RESET_PASSWORD_WINDOW',
      'integer',
      'Password-reset email request window in minutes. Default: 2.',
      '# RESET_PASSWORD_WINDOW=2',
    ],
    [
      'RESET_PASSWORD_SUBMISSION_MAX',
      'integer',
      'Maximum password-reset token submissions per IP. Defaults to RESET_PASSWORD_MAX, then 2.',
      '# RESET_PASSWORD_SUBMISSION_MAX=2',
    ],
    [
      'RESET_PASSWORD_SUBMISSION_WINDOW',
      'integer',
      'Password-reset token submission window in minutes. Defaults to RESET_PASSWORD_WINDOW, then 2.',
      '# RESET_PASSWORD_SUBMISSION_WINDOW=2',
    ],
    [
      'VERIFY_EMAIL_MAX',
      'integer',
      'Maximum verification-email resend requests per IP in VERIFY_EMAIL_WINDOW. Default: 2.',
      '# VERIFY_EMAIL_MAX=2',
    ],
    [
      'VERIFY_EMAIL_WINDOW',
      'integer',
      'Verification-email resend window in minutes. Default: 2.',
      '# VERIFY_EMAIL_WINDOW=2',
    ],
    [
      'VERIFY_EMAIL_SUBMISSION_MAX',
      'integer',
      'Maximum email-verification token submissions per IP. Defaults to VERIFY_EMAIL_MAX, then 2.',
      '# VERIFY_EMAIL_SUBMISSION_MAX=2',
    ],
    [
      'VERIFY_EMAIL_SUBMISSION_WINDOW',
      'integer',
      'Email-verification token submission window in minutes. Defaults to VERIFY_EMAIL_WINDOW, then 2.',
      '# VERIFY_EMAIL_SUBMISSION_WINDOW=2',
    ],
  ]}
/>

#### Score for each violation

<OptionTable
  options={[
    ['LOGIN_VIOLATION_SCORE', 'integer', 'Score for login violations.', 'LOGIN_VIOLATION_SCORE=1'],
    [
      'REGISTRATION_VIOLATION_SCORE',
      'integer',
      'Score for registration violations.',
      'REGISTRATION_VIOLATION_SCORE=1',
    ],
    [
      'CONCURRENT_VIOLATION_SCORE',
      'integer',
      'Score for concurrent violations.',
      'CONCURRENT_VIOLATION_SCORE=1',
    ],
    [
      'MESSAGE_VIOLATION_SCORE',
      'integer',
      'Score for message violations.',
      'MESSAGE_VIOLATION_SCORE=1',
    ],
    [
      'NON_BROWSER_VIOLATION_SCORE',
      'integer',
      'Score for non-browser violations.',
      'NON_BROWSER_VIOLATION_SCORE=20',
    ],
    [
      'ILLEGAL_MODEL_REQ_SCORE',
      'integer',
      'Score for illegal model requests.',
      'ILLEGAL_MODEL_REQ_SCORE=5',
    ],
    [
      'IMPORT_VIOLATION_SCORE',
      'integer',
      'Score for import conversation violations.',
      'IMPORT_VIOLATION_SCORE=1',
    ],
    [
      'FORK_VIOLATION_SCORE',
      'integer',
      'Score for conversation fork violations.',
      'FORK_VIOLATION_SCORE=1',
    ],
    [
      'TTS_VIOLATION_SCORE',
      'integer',
      'Score for text-to-speech violations.',
      'TTS_VIOLATION_SCORE=0',
    ],
    [
      'STT_VIOLATION_SCORE',
      'integer',
      'Score for speech-to-text violations.',
      'STT_VIOLATION_SCORE=0',
    ],
    [
      'FILE_UPLOAD_VIOLATION_SCORE',
      'integer',
      'Score for file upload violations.',
      'FILE_UPLOAD_VIOLATION_SCORE=0',
    ],
    [
      'RESET_PASSWORD_VIOLATION_SCORE',
      'integer',
      'Score for password-reset email request violations. Default: 1.',
      '# RESET_PASSWORD_VIOLATION_SCORE=1',
    ],
    [
      'VERIFY_EMAIL_VIOLATION_SCORE',
      'integer',
      'Score for verification-email resend violations. Default: 1.',
      '# VERIFY_EMAIL_VIOLATION_SCORE=1',
    ],
    [
      'RESET_PASSWORD_SUBMISSION_VIOLATION_SCORE',
      'integer',
      'Score for password-reset token submission violations. Default: 1.',
      '# RESET_PASSWORD_SUBMISSION_VIOLATION_SCORE=1',
    ],
    [
      'VERIFY_EMAIL_SUBMISSION_VIOLATION_SCORE',
      'integer',
      'Score for email-verification token submission violations. Default: 1.',
      '# VERIFY_EMAIL_SUBMISSION_VIOLATION_SCORE=1',
    ],
    [
      'TOOL_CALL_VIOLATION_SCORE',
      'integer',
      'Score for tool call violations.',
      'TOOL_CALL_VIOLATION_SCORE=0',
    ],
    [
      'CONVO_ACCESS_VIOLATION_SCORE',
      'integer',
      'Score for conversation access violations.',
      'CONVO_ACCESS_VIOLATION_SCORE=0',
    ],
  ]}
/>

> Note: Non-browser access and Illegal model requests are almost always nefarious as it means a 3rd party is attempting to access the server through an automated script.

#### Message rate limiting (per user & IP)

<OptionTable
  options={[
    [
      'LIMIT_CONCURRENT_MESSAGES',
      'boolean',
      'Whether to limit the amount of messages a user can send per request.',
      'LIMIT_CONCURRENT_MESSAGES=true',
    ],
    [
      'CONCURRENT_MESSAGE_MAX',
      'integer',
      'The max amount of messages a user can send per request.',
      'CONCURRENT_MESSAGE_MAX=2',
    ],
  ]}
/>

#### Limiters

> Note: You can utilize both limiters, but default is to limit by IP only.

##### IP Limiter:

<OptionTable
  options={[
    [
      'LIMIT_MESSAGE_IP',
      'boolean',
      'Whether to limit the amount of messages an IP can send per `MESSAGE_IP_WINDOW`.',
      'LIMIT_MESSAGE_IP=true',
    ],
    [
      'MESSAGE_IP_MAX',
      'integer',
      'The max amount of messages an IP can send per `MESSAGE_IP_WINDOW`.',
      'MESSAGE_IP_MAX=40',
    ],
    [
      'MESSAGE_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `MESSAGE_IP_MAX` messages.',
      'MESSAGE_IP_WINDOW=1',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'LIMIT_MESSAGE_USER',
      'boolean',
      'Whether to limit the amount of messages an user can send per `MESSAGE_USER_WINDOW`.',
      'LIMIT_MESSAGE_USER=false',
    ],
    [
      'MESSAGE_USER_MAX',
      'integer',
      'The max amount of messages an user can send per `MESSAGE_USER_WINDOW`.',
      'MESSAGE_USER_MAX=40',
    ],
    [
      'MESSAGE_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `MESSAGE_USER_MAX` messages.',
      'MESSAGE_USER_WINDOW=1',
    ],
  ]}
/>

Confirmed idempotent Agent-generation retries bypass ordinary per-user message admission so recovery can proceed, but they still pass through the shared IP limiter when `LIMIT_MESSAGE_IP` is enabled. Trusted Agent-trigger deliveries remain exempt from that browser-facing IP boundary.

##### Queued Attachment TTL Limiter:

The `/files/usage` endpoint renews a bounded TTL hold for attachments waiting in queued Agent messages. It has a separate per-user limiter so metadata renewals do not consume the upload quota.

<OptionTable
  options={[
    [
      'FILE_USAGE_USER_MAX',
      'integer',
      'Maximum queued-attachment TTL renewal requests per user and window. Default: 120.',
      '# FILE_USAGE_USER_MAX=120',
    ],
    [
      'FILE_USAGE_USER_WINDOW',
      'integer',
      'Window in minutes for FILE_USAGE_USER_MAX. Default: 15.',
      '# FILE_USAGE_USER_WINDOW=15',
    ],
  ]}
/>

#### Import conversation rate limiting

Limits how often users can import conversations to prevent abuse.

> Note: You can utilize both limiters, but default is to limit by IP only.

##### IP Limiter:

<OptionTable
  options={[
    [
      'LIMIT_IMPORT_IP',
      'boolean',
      'Whether to limit the amount of conversation imports an IP can perform per `IMPORT_IP_WINDOW`.',
      'LIMIT_IMPORT_IP=true',
    ],
    [
      'IMPORT_IP_MAX',
      'integer',
      'The max amount of conversation imports an IP can perform per `IMPORT_IP_WINDOW`.',
      'IMPORT_IP_MAX=100',
    ],
    [
      'IMPORT_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `IMPORT_IP_MAX` imports.',
      'IMPORT_IP_WINDOW=1',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'LIMIT_IMPORT_USER',
      'boolean',
      'Whether to limit the amount of conversation imports a user can perform per `IMPORT_USER_WINDOW`.',
      'LIMIT_IMPORT_USER=false',
    ],
    [
      'IMPORT_USER_MAX',
      'integer',
      'The max amount of conversation imports a user can perform per `IMPORT_USER_WINDOW`.',
      'IMPORT_USER_MAX=50',
    ],
    [
      'IMPORT_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `IMPORT_USER_MAX` imports.',
      'IMPORT_USER_WINDOW=1',
    ],
  ]}
/>

#### Conversation forking rate limiting

Limits how often users can fork conversations to prevent abuse.

> Note: You can utilize both limiters, but default is to limit by IP only.

##### IP Limiter:

<OptionTable
  options={[
    [
      'LIMIT_FORK_IP',
      'boolean',
      'Whether to limit the amount of conversation forks an IP can create per `FORK_IP_WINDOW`.',
      'LIMIT_FORK_IP=true',
    ],
    [
      'FORK_IP_MAX',
      'integer',
      'The max amount of conversation forks an IP can create per `FORK_IP_WINDOW`.',
      'FORK_IP_MAX=30',
    ],
    [
      'FORK_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `FORK_IP_MAX` forks.',
      'FORK_IP_WINDOW=1',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'LIMIT_FORK_USER',
      'boolean',
      'Whether to limit the amount of conversation forks a user can create per `FORK_USER_WINDOW`.',
      'LIMIT_FORK_USER=false',
    ],
    [
      'FORK_USER_MAX',
      'integer',
      'The max amount of conversation forks a user can create per `FORK_USER_WINDOW`.',
      'FORK_USER_MAX=7',
    ],
    [
      'FORK_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `FORK_USER_MAX` forks.',
      'FORK_USER_WINDOW=1',
    ],
  ]}
/>

#### File upload rate limiting

Limits how often users can upload files to prevent abuse.

> Note: These can also be configured via `librechat.yaml` in the `rateLimits.fileUploads` section.

##### IP Limiter:

<OptionTable
  options={[
    [
      'FILE_UPLOAD_IP_MAX',
      'integer',
      'Max file uploads per IP per `FILE_UPLOAD_IP_WINDOW`. Default: 100.',
      '# FILE_UPLOAD_IP_MAX=100',
    ],
    [
      'FILE_UPLOAD_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `FILE_UPLOAD_IP_MAX`. Default: 15.',
      '# FILE_UPLOAD_IP_WINDOW=15',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'FILE_UPLOAD_USER_MAX',
      'integer',
      'Max file uploads per user per `FILE_UPLOAD_USER_WINDOW`. Default: 50.',
      '# FILE_UPLOAD_USER_MAX=50',
    ],
    [
      'FILE_UPLOAD_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `FILE_UPLOAD_USER_MAX`. Default: 15.',
      '# FILE_UPLOAD_USER_WINDOW=15',
    ],
  ]}
/>

#### TTS (Text-to-Speech) rate limiting

Limits how often users can use Text-to-Speech to prevent abuse.

> Note: These can also be configured via `librechat.yaml` in the `rateLimits.tts` section.

##### IP Limiter:

<OptionTable
  options={[
    [
      'TTS_IP_MAX',
      'integer',
      'Max TTS requests per IP per `TTS_IP_WINDOW`. Default: 100.',
      '# TTS_IP_MAX=100',
    ],
    [
      'TTS_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `TTS_IP_MAX`. Default: 1.',
      '# TTS_IP_WINDOW=1',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'TTS_USER_MAX',
      'integer',
      'Max TTS requests per user per `TTS_USER_WINDOW`. Default: 50.',
      '# TTS_USER_MAX=50',
    ],
    [
      'TTS_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `TTS_USER_MAX`. Default: 1.',
      '# TTS_USER_WINDOW=1',
    ],
  ]}
/>

#### STT (Speech-to-Text) rate limiting

Limits how often users can use Speech-to-Text to prevent abuse.

> Note: These can also be configured via `librechat.yaml` in the `rateLimits.stt` section.

##### IP Limiter:

<OptionTable
  options={[
    [
      'STT_IP_MAX',
      'integer',
      'Max STT requests per IP per `STT_IP_WINDOW`. Default: 100.',
      '# STT_IP_MAX=100',
    ],
    [
      'STT_IP_WINDOW',
      'integer',
      'In minutes, determines the window of time for `STT_IP_MAX`. Default: 1.',
      '# STT_IP_WINDOW=1',
    ],
  ]}
/>

##### User Limiter:

<OptionTable
  options={[
    [
      'STT_USER_MAX',
      'integer',
      'Max STT requests per user per `STT_USER_WINDOW`. Default: 50.',
      '# STT_USER_MAX=50',
    ],
    [
      'STT_USER_WINDOW',
      'integer',
      'In minutes, determines the window of time for `STT_USER_MAX`. Default: 1.',
      '# STT_USER_WINDOW=1',
    ],
  ]}
/>

### Balance

The following feature allows for the management of user balances within the system's endpoints. You have the option to add balances manually, or you may choose to implement a system that accumulates balances automatically for users. If a specific initial balance is defined in the configuration, tokens will be credited to the user's balance automatically when they register.

see: **[Token Usage](/docs/configuration/token_usage)**

<OptionTable
  options={[
    [
      'CHECK_BALANCE',
      'boolean',
      'Enable token credit balances for the OpenAI/Plugins endpoints.',
      'CHECK_BALANCE=false',
    ],
    [
      'START_BALANCE',
      'integer',
      "If the value is set, tokens will be credited to the user's balance after registration.",
      'START_BALANCE=20000',
    ],
  ]}
/>

#### Managing Balances

- Run `npm run add-balance` to manually add balances.
  - You can also specify the email and token credit amount to add, e.g.: `npm run add-balance example@example.com 1000`
- Run `npm run set-balance` to manually set balances, similar to `add-balance`.
- Run `npm run list-balances` to list the balance of every user.

> **Note:** 1000 credits = $0.001 (1 mill USD)

### Registration and Login

see: **[Authentication System](/docs/configuration/authentication)**

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/4c51dc25-31d3-4c51-8c2a-0cdfb5a25033"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/3bc5371d-e51d-4e91-ac68-56db6e85bb2c"
  alt="User registration screen"
/>

<Callout type="info" title="Configuration File Clarification">
  All authentication settings in this section should be configured in your `.env` file, not in the
  `librechat.yaml` file or `docker-compose.override.yml`. The `docker-compose.override.yml` file is
  only used to mount volumes and set environment variables for Docker, while the `librechat.yaml`
  file is used for custom endpoints and other application settings.
</Callout>

- General Settings:

<OptionTable
  options={[
    [
      'ALLOW_EMAIL_LOGIN',
      'boolean',
      'Enable or disable ONLY email login.',
      'ALLOW_EMAIL_LOGIN=true',
    ],
    [
      'ALLOW_EMAIL_LOGIN_OVERRIDE',
      'boolean',
      'Permit direct API email login while ALLOW_EMAIL_LOGIN=false. Each use is logged. Default: false.',
      '# ALLOW_EMAIL_LOGIN_OVERRIDE=false',
    ],
    [
      'ALLOW_REGISTRATION',
      'boolean',
      'Enable or disable Email registration of new users.',
      'ALLOW_REGISTRATION=true',
    ],
    [
      'ALLOW_SOCIAL_LOGIN',
      'boolean',
      'Allow users to connect to LibreChat with various social networks.',
      'ALLOW_SOCIAL_LOGIN=false',
    ],
    [
      'ALLOW_SOCIAL_REGISTRATION',
      'boolean',
      'Enable or disable registration of new users using various social networks.',
      'ALLOW_SOCIAL_REGISTRATION=false',
    ],
    [
      'ALLOW_PASSWORD_RESET',
      'boolean',
      'Enable or disable the ability for users to reset their password by themselves',
      'ALLOW_PASSWORD_RESET=false',
    ],
    [
      'ALLOW_ACCOUNT_DELETION',
      'boolean',
      'Enable or disable the ability for users to delete their account by themselves. Enabled by default if omitted/commented out',
      'ALLOW_ACCOUNT_DELETION=true',
    ],
    [
      'ALLOW_UNVERIFIED_EMAIL_LOGIN',
      'boolean',
      'Set to true to allow users to log in without verifying their email address. If set to false, users will be required to verify their email before logging in.',
      'ALLOW_UNVERIFIED_EMAIL_LOGIN=true',
    ],
    [
      'MIN_PASSWORD_LENGTH',
      'number',
      'Minimum password length for user authentication. When using LDAP authentication, you may want to set this to 1 to bypass local password validation, as LDAP servers handle their own password policies.',
      'MIN_PASSWORD_LENGTH=8',
    ],
  ]}
/>

> **Quick Tip:** Even with registration disabled, add users directly to the database using `npm run create-user`.
> **Quick Tip:** With registration disabled, you can delete a user with `npm run delete-user email@domain.com`.

- Session and Refresh Token Settings:

<OptionTable
  options={[
    [
      'SESSION_EXPIRY',
      'integer (milliseconds)',
      'Session expiry time.',
      'SESSION_EXPIRY=1000 * 60 * 15',
    ],
    [
      'REFRESH_TOKEN_EXPIRY',
      'integer (milliseconds)',
      'Refresh token expiry time.',
      'REFRESH_TOKEN_EXPIRY=(1000 * 60 * 60 * 24) * 7',
    ],
    [
      'SESSION_COOKIE_SECURE',
      'boolean',
      'Overrides the Secure attribute for session/auth cookies. Leave unset to use the default NODE_ENV/DOMAIN_SERVER heuristic.',
      '# SESSION_COOKIE_SECURE=false',
    ],
  ]}
/>

- For more information: **[Refresh Token](https://github.com/danny-avila/LibreChat/pull/927)**

- JWT Settings:

Use unique values of at least 32 bytes. Generate permanent values for production with the [Credentials Generator](/toolkit/creds_generator).

<OptionTable
  options={[
    [
      'JWT_SECRET',
      'string (hex)',
      'JWT secret key.',
      'JWT_SECRET=',
    ],
    [
      'JWT_REFRESH_SECRET',
      'string (hex)',
      'JWT refresh secret key.',
      'JWT_REFRESH_SECRET=',
    ],
  ]}
/>

Blank JWT secrets use the same temporary-credential bootstrap described under [Credentials Configuration](#credentials-configuration). In production and horizontally scaled deployments, set permanent values and share them across replicas so issued sessions remain valid.

### Social Logins

For more details: [OAuth2-OIDC](/docs/configuration/authentication/OAuth2-OIDC)

#### Apple Authentication

For more information: **[Apple Authentication](/docs/configuration/authentication/OAuth2-OIDC/apple)**

<OptionTable
  options={[
    [
      'APPLE_CLIENT_ID',
      'string',
      'Your Apple Services ID (e.g., com.yourdomain.librechat.services).',
      'APPLE_CLIENT_ID=com.yourdomain.librechat.services',
    ],
    ['APPLE_TEAM_ID', 'string', 'Your Apple Developer Team ID.', 'APPLE_TEAM_ID=YOUR_TEAM_ID'],
    [
      'APPLE_KEY_ID',
      'string',
      'Your Apple Key ID from the downloaded key.',
      'APPLE_KEY_ID=YOUR_KEY_ID',
    ],
    [
      'APPLE_PRIVATE_KEY_PATH',
      'string',
      'Absolute path to your downloaded .p8 file.',
      'APPLE_PRIVATE_KEY_PATH=/path/to/AuthKey.p8',
    ],
    [
      'APPLE_CALLBACK_URL',
      'string',
      'The callback URL for Apple authentication.',
      'APPLE_CALLBACK_URL=/oauth/apple/callback',
    ],
  ]}
/>

#### Discord Authentication

For more information: **[Discord](/docs/configuration/authentication/OAuth2-OIDC/discord)**

<OptionTable
  options={[
    ['DISCORD_CLIENT_ID', 'string', 'Your Discord client ID.', 'DISCORD_CLIENT_ID='],
    ['DISCORD_CLIENT_SECRET', 'string', 'Your Discord client secret.', 'DISCORD_CLIENT_SECRET='],
    [
      'DISCORD_CALLBACK_URL',
      'string',
      'The callback URL for Discord authentication.',
      'DISCORD_CALLBACK_URL=/oauth/discord/callback',
    ],
  ]}
/>

#### Facebook Authentication

For more information: **[Facebook Authentication](/docs/configuration/authentication/OAuth2-OIDC/facebook)**

<OptionTable
  options={[
    ['FACEBOOK_CLIENT_ID', 'string', 'Your Facebook client ID.', 'FACEBOOK_CLIENT_ID='],
    ['FACEBOOK_CLIENT_SECRET', 'string', 'Your Facebook client secret.', 'FACEBOOK_CLIENT_SECRET='],
    [
      'FACEBOOK_CALLBACK_URL',
      'string',
      'The callback URL for Facebook authentication.',
      'FACEBOOK_CALLBACK_URL=/oauth/facebook/callback',
    ],
  ]}
/>

#### GitHub Authentication

For more information: **[GitHub Authentication](/docs/configuration/authentication/OAuth2-OIDC/github)**

<OptionTable
  options={[
    ['GITHUB_CLIENT_ID', 'string', 'Your GitHub client ID.', 'GITHUB_CLIENT_ID='],
    ['GITHUB_CLIENT_SECRET', 'string', 'Your GitHub client secret.', 'GITHUB_CLIENT_SECRET='],
    [
      'GITHUB_CALLBACK_URL',
      'string',
      'The callback URL for GitHub authentication.',
      'GITHUB_CALLBACK_URL=/oauth/github/callback',
    ],
    [
      'GITHUB_ENTERPRISE_BASE_URL',
      'string',
      'Optional: The base URL for your GitHub Enterprise instance.',
      'GITHUB_ENTERPRISE_BASE_URL=',
    ],
    [
      'GITHUB_ENTERPRISE_USER_AGENT',
      'string',
      'Optional: The user agent for GitHub Enterprise requests.',
      'GITHUB_ENTERPRISE_USER_AGENT=',
    ],
  ]}
/>

#### Google Authentication

For more information: **[Google Authentication](/docs/configuration/authentication/OAuth2-OIDC/google)**

<OptionTable
  options={[
    ['GOOGLE_CLIENT_ID', 'string', 'Your Google client ID.', 'GOOGLE_CLIENT_ID='],
    ['GOOGLE_CLIENT_SECRET', 'string', 'Your Google client secret.', 'GOOGLE_CLIENT_SECRET='],
    [
      'GOOGLE_CALLBACK_URL',
      'string',
      'The callback URL for Google authentication.',
      'GOOGLE_CALLBACK_URL=/oauth/google/callback',
    ],
  ]}
/>

#### OpenID Connect

For more information:

- [Auth0](/docs/configuration/authentication/OAuth2-OIDC/auth0)
- [AWS Cognito](/docs/configuration/authentication/OAuth2-OIDC/aws)
- [Azure Entra/AD](/docs/configuration/authentication/OAuth2-OIDC/azure)
- [Keycloak](/docs/configuration/authentication/OAuth2-OIDC/keycloak)

<OptionTable
  options={[
    ['OPENID_CLIENT_ID', 'string', 'Your OpenID client ID.', 'OPENID_CLIENT_ID='],
    ['OPENID_CLIENT_SECRET', 'string', 'Your OpenID client secret.', 'OPENID_CLIENT_SECRET='],
    ['OPENID_ISSUER', 'string', 'The OpenID issuer URL.', 'OPENID_ISSUER='],
    [
      'OPENID_SESSION_SECRET',
      'string',
      'The secret for OpenID session storage.',
      'OPENID_SESSION_SECRET=',
    ],
    ['OPENID_SCOPE', 'string', 'The OpenID scope.', 'OPENID_SCOPE="openid profile email"'],
    [
      'OPENID_CALLBACK_URL',
      'string',
      'The callback URL for OpenID authentication.',
      'OPENID_CALLBACK_URL=/oauth/openid/callback',
    ],
    [
      'OPENID_AUDIENCE',
      'string',
      'Audience value for OpenID JWT validation and authorization requests. Comma-separated values are accepted for JWT validation; authorization requests use the first non-empty value. Required for Auth0 when using OPENID_REUSE_TOKENS=true to receive JWT access tokens instead of opaque tokens.',
      'OPENID_AUDIENCE=https://api.librechat.com',
    ],
    [
      'OPENID_REQUIRED_ROLE',
      'string',
      'The required role(s) for validation. Supports a single role or multiple comma-separated roles. When multiple roles are specified, the user needs ANY of the specified roles (OR logic).',
      'OPENID_REQUIRED_ROLE=admin or OPENID_REQUIRED_ROLE=role1,role2,admin',
    ],
    [
      'OPENID_REQUIRED_ROLE_TOKEN_KIND',
      'string',
      'The token kind for required role validation.',
      'OPENID_REQUIRED_ROLE_TOKEN_KIND=',
    ],
    [
      'OPENID_REQUIRED_ROLE_PARAMETER_PATH',
      'string',
      'The parameter path for required role validation.',
      'OPENID_REQUIRED_ROLE_PARAMETER_PATH=',
    ],
    [
      'OPENID_ADMIN_ROLE',
      'string',
      'The role the user should have in order to be an admin in LibreChat.',
      'OPENID_ADMIN_ROLE=',
    ],
    [
      'OPENID_ADMIN_ROLE_TOKEN_KIND',
      'string',
      'The source of the information for admin role verification. Possible values are: access, id or userinfo.',
      'OPENID_ADMIN_ROLE_TOKEN_KIND=',
    ],
    [
      'OPENID_ADMIN_ROLE_PARAMETER_PATH',
      'string',
      'The parameter path for required role validation.',
      'OPENID_ADMIN_ROLE_PARAMETER_PATH=',
    ],
    [
      'OPENID_ROLE_SYNC_ENABLED',
      'boolean',
      'Enable generic OpenID role sync for non-admin roles. ADMIN cannot be assigned by role sync; use OPENID_ADMIN_ROLE for admin elevation.',
      'OPENID_ROLE_SYNC_ENABLED=false',
    ],
    [
      'OPENID_ROLE_SYNC_API_ENABLED',
      'boolean',
      'Enable API-based role sync helpers. Requires OPENID_ROLE_SYNC_ENABLED=true.',
      'OPENID_ROLE_SYNC_API_ENABLED=false',
    ],
    [
      'OPENID_ROLE_SYNC_SOURCE',
      'string',
      'Token source for the role claim. Must be one of: access, id, userinfo. Default: id.',
      'OPENID_ROLE_SYNC_SOURCE=id',
    ],
    [
      'OPENID_ROLE_SYNC_CLAIM',
      'string',
      'Claim path that contains the provider roles or groups. Required when role sync is enabled.',
      'OPENID_ROLE_SYNC_CLAIM=',
    ],
    [
      'OPENID_ROLE_SYNC_ROLE_PRIORITY',
      'string',
      'Comma-separated LibreChat roles ordered from most important to least important. The first matching role is assigned.',
      'OPENID_ROLE_SYNC_ROLE_PRIORITY=Support,User',
    ],
    [
      'OPENID_ROLE_SYNC_FALLBACK_ROLE',
      'string',
      'LibreChat role assigned when no priority role matches. The fallback is authoritative when configured. Remote Agents API authentication treats an unresolved Entra group-overage list as empty, so this fallback applies there as well.',
      'OPENID_ROLE_SYNC_FALLBACK_ROLE=USER',
    ],
    [
      'OPENID_BUTTON_LABEL',
      'string',
      'The label for the OpenID login button.',
      'OPENID_BUTTON_LABEL=',
    ],
    [
      'OPENID_IMAGE_URL',
      'string',
      'The URL of the OpenID login button image.',
      'OPENID_IMAGE_URL=',
    ],
    [
      'OPENID_USE_END_SESSION_ENDPOINT',
      'string',
      'Whether to use the Issuer End Session Endpoint as a Logout Redirect',
      'OPENID_USE_END_SESSION_ENDPOINT=TRUE',
    ],
    [
      'OPENID_MAX_LOGOUT_URL_LENGTH',
      'number',
      'Maximum logout URL length before using logout_hint instead of id_token_hint. Default: 2000.',
      '# OPENID_MAX_LOGOUT_URL_LENGTH=2000',
    ],
    [
      'OPENID_AUTO_REDIRECT',
      'boolean',
      'Whether to automatically redirect to the OpenID provider.',
      'OPENID_AUTO_REDIRECT=true',
    ],
    [
      'OPENID_USE_PKCE',
      'boolean',
      'Use PKCE (Proof Key for Code Exchange) for OpenID authentication. For public clients without a client secret, leave OPENID_CLIENT_SECRET empty and set this to true.',
      '# OPENID_USE_PKCE=true',
    ],
    [
      'OPENID_POST_LOGOUT_REDIRECT_URI',
      'string',
      'Redirect URI after OpenID logout. Defaults to ${DOMAIN_CLIENT}/login.',
      '# OPENID_POST_LOGOUT_REDIRECT_URI=',
    ],
    [
      'OPENID_CLOCK_TOLERANCE',
      'number',
      'Clock tolerance in seconds for token validation. Default: 300.',
      '# OPENID_CLOCK_TOLERANCE=300',
    ],
    [
      'OPENID_GENERATE_NONCE',
      'boolean',
      'Force the OpenID client to generate a nonce parameter. Required by some identity providers like AWS Cognito (especially with federation) and Authentik.',
      'OPENID_GENERATE_NONCE=true',
    ],
    [
      'DEBUG_OPENID_REQUESTS',
      'boolean',
      'Enable detailed logging of OpenID request headers. When disabled (default), only request URLs are logged at debug level. When enabled, request headers are also logged (with sensitive data masked) for deeper debugging of authentication issues.',
      'DEBUG_OPENID_REQUESTS=false',
    ],
    [
      'OPENID_USERNAME_CLAIM',
      'string',
      "The user info property from the OpenID provider to store as the user's username.",
      'OPENID_USERNAME_CLAIM=',
    ],
    [
      'OPENID_NAME_CLAIM',
      'string',
      "The user info property from the OpenID provider to store as the user's display name.",
      'OPENID_NAME_CLAIM=',
    ],
    [
      'OPENID_EMAIL_CLAIM',
      'string',
      'The user info claim to use as the email/identifier for user matching (e.g., "upn" for Entra ID). When not set, defaults to: email → preferred_username → upn.',
      'OPENID_EMAIL_CLAIM=',
    ],
  ]}
/>

<Callout type="warning" title="OpenID role sync">
  `OPENID_ROLE_SYNC_CLAIM` is required when role sync is enabled.
  `OPENID_ROLE_SYNC_API_ENABLED=true` also requires `OPENID_ROLE_SYNC_ENABLED=true`. Generic role
  sync cannot assign `ADMIN`; use `OPENID_ADMIN_ROLE` for admin elevation.
</Callout>

##### OpenID Connect Token Reuse

LibreChat supports reusing access and refresh tokens issued by your OpenID Connect provider (like Azure Entra ID or Auth0) to manage user authentication state. When this feature is active, the refresh token passed to the user as a cookie is issued by your OpenID provider instead of LibreChat.

<OptionTable
  options={[
    [
      'OPENID_REUSE_TOKENS',
      'boolean',
      'Enable reuse of OpenID provider tokens for session management.',
      'OPENID_REUSE_TOKENS=false',
    ],
    [
      'OPENID_SCOPE',
      'string',
      'Space-separated list of OpenID scopes. Must include offline_access for token reuse.',
      'OPENID_SCOPE=api://librechat/.default openid profile email offline_access',
    ],
    [
      'OPENID_AUDIENCE',
      'string',
      'Audience value for OpenID JWT validation and authorization requests. Comma-separated values are accepted for JWT validation; authorization requests use the first non-empty value. Required for Auth0 when OPENID_REUSE_TOKENS=true. See the note in the main OpenID section above.',
      'OPENID_AUDIENCE=https://api.librechat.com',
    ],
    [
      'OPENID_REUSE_MAX_SESSION_AGE_MS',
      'number',
      'Maximum age a reused OpenID session token is served before LibreChat forces an IdP refresh. Default: 900000 ms / 15 minutes.',
      'OPENID_REUSE_MAX_SESSION_AGE_MS=900000',
    ],
    [
      'OPENID_REFRESH_BRIDGE_GRACE_MS',
      'number',
      'Short recovery window for a rotated refresh token while LibreChat publishes the refreshed OpenID session. Default: 60000 ms / 1 minute.',
      'OPENID_REFRESH_BRIDGE_GRACE_MS=60000',
    ],
    [
      'OPENID_JWKS_URL_CACHE_ENABLED',
      'boolean',
      'Enable caching of signing key verification results.',
      'OPENID_JWKS_URL_CACHE_ENABLED=true',
    ],
    [
      'OPENID_JWKS_URL_CACHE_TIME',
      'number',
      'Cache duration in milliseconds (default: 600000 ms / 10 minutes).',
      'OPENID_JWKS_URL_CACHE_TIME=600000',
    ],
    [
      'OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED',
      'boolean',
      'Enable on-behalf-of flow for user info.',
      'OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true',
    ],
    [
      'OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE',
      'string',
      'Scope for user info in on-behalf-of flow.',
      'OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE=user.read',
    ],
    [
      'OPENID_USE_END_SESSION_ENDPOINT',
      'boolean',
      'Enable use of the end session endpoint for logout.',
      'OPENID_USE_END_SESSION_ENDPOINT=true',
    ],
    [
      'OPENID_MAX_LOGOUT_URL_LENGTH',
      'number',
      'Maximum logout URL length in characters before switching to logout_hint. Useful to prevent URI too long errors when id_token_hint exceeds server limits. Default: 2000.',
      'OPENID_MAX_LOGOUT_URL_LENGTH=2000',
    ],
  ]}
/>

`OPENID_REUSE_MAX_SESSION_AGE_MS` and `OPENID_REFRESH_BRIDGE_GRACE_MS` accept arithmetic expressions like `SESSION_EXPIRY`. Increase the session age toward the IdP access-token lifetime when your provider revokes the previous access token on refresh, so downstream consumers such as MCP servers can finish using a still-valid bearer token. Increase the bridge grace period only when slow session persistence or cross-replica publication needs more than the default minute to publish a rotated token.

<Callout type="note" title="Note">
  For detailed configuration steps and prerequisites, see [Re-use OpenID Tokens for Login
  Session](/docs/configuration/authentication/OAuth2-OIDC/token-reuse).
</Callout>

##### Microsoft Graph API / Entra ID Integration

When using Azure Entra ID (formerly Azure AD) as your OpenID provider, you can enable additional Microsoft Graph API features for enhanced people and group search capabilities within the permissions and sharing system.

<OptionTable
  options={[
    [
      'USE_ENTRA_ID_FOR_PEOPLE_SEARCH',
      'boolean',
      'Enable Entra ID people search integration in permissions/sharing system. When enabled, the people picker will search both local database and Entra ID.',
      'USE_ENTRA_ID_FOR_PEOPLE_SEARCH=false',
    ],
    [
      'ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS',
      'boolean',
      'When enabled, Entra ID group owners will be considered as members of the group.',
      'ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS=false',
    ],
    [
      'OPENID_GRAPH_SCOPES',
      'string',
      'Microsoft Graph API scopes needed for people/group search. Default scopes provide access to user profiles and group memberships.',
      'OPENID_GRAPH_SCOPES=User.Read,People.Read,GroupMember.Read.All,User.ReadBasic.All',
    ],
    [
      'GRAPH_API_SCOPES',
      'string',
      'Space-separated Microsoft Graph scopes requested by the OBO exchange for {{LIBRECHAT_GRAPH_ACCESS_TOKEN}} placeholders in YAML-defined MCP servers. Default: https://graph.microsoft.com/.default.',
      '# GRAPH_API_SCOPES=https://graph.microsoft.com/.default',
    ],
  ]}
/>

`GRAPH_API_SCOPES` is separate from `OPENID_GRAPH_SCOPES`: the former controls Graph tokens resolved into MCP configuration, while the latter controls Entra ID people and group search.

<Callout type="warning" title="Important Prerequisites">
  - You must have Azure Entra ID configured as your OpenID provider - **OpenID token reuse MUST be
  enabled** (`OPENID_REUSE_TOKENS=true`) - this feature will not work without it - Your Azure app
  registration must have the appropriate Microsoft Graph API permissions - For group search
  functionality, admin consent may be required for certain Graph API scopes
</Callout>

##### SharePoint Integration

LibreChat supports direct integration with SharePoint Online and OneDrive for Business, allowing users to select and attach files from their SharePoint libraries directly within conversations. This enterprise feature leverages the existing Azure Entra ID authentication.

<OptionTable
  options={[
    [
      'ENABLE_SHAREPOINT_FILEPICKER',
      'boolean',
      'Enable SharePoint file picker in chat and agent panels. When enabled, adds "From SharePoint" option in file attachment menu.',
      'ENABLE_SHAREPOINT_FILEPICKER=true',
    ],
    [
      'SHAREPOINT_BASE_URL',
      'string',
      'SharePoint tenant base URL. Required when SharePoint integration is enabled.',
      'SHAREPOINT_BASE_URL=https://yourtenant.sharepoint.com',
    ],
    [
      'SHAREPOINT_PICKER_SHAREPOINT_SCOPE',
      'string',
      'SharePoint-specific OAuth scope for the file picker. Used for authentication when opening the SharePoint file picker interface.',
      'SHAREPOINT_PICKER_SHAREPOINT_SCOPE=https://yourtenant.sharepoint.com/AllSites.Read',
    ],
    [
      'SHAREPOINT_PICKER_GRAPH_SCOPE',
      'string',
      'Microsoft Graph API scope for file downloads. Used for downloading files from SharePoint after selection.',
      'SHAREPOINT_PICKER_GRAPH_SCOPE=Files.Read.All',
    ],
  ]}
/>

<Callout type="error" title="Critical Requirements">
**All of the following must be configured for SharePoint integration to work:**
- Azure Entra ID authentication must be fully configured
- **`OPENID_REUSE_TOKENS=true`** is mandatory (uses on-behalf-of token flow)
- `OPENID_SCOPE` must include your LibreChat app API scope, for example `api://<client-id>/access_as_user`
- `OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true` is required when using that app-audience scope with Azure Entra ID
- Your Azure app registration must have SharePoint and Graph API permissions
- Your Azure app registration must expose the LibreChat API scope used in `OPENID_SCOPE`
- All four SharePoint environment variables must be set
- HTTPS is required in production environments
</Callout>

<Callout type="info" title="Feature Capabilities">
  When enabled, users can: - Access files from SharePoint document libraries and OneDrive for
  Business - Select multiple files at once (default max: 10 files) - See real-time download progress
  - Files are downloaded and attached to the conversation like regular uploads
</Callout>

For detailed SharePoint configuration instructions, see: [SharePoint Integration Guide](/docs/configuration/sharepoint)

#### SAML

For more information:

- [Auth0](/docs/configuration/authentication/SAML/auth0)

<Callout type="warning" title="Mutual Exclusion of OpenID and SAML">
If OpenID is enabled, SAML authentication will be automatically disabled.

Only one authentication method can be active at a time.

</Callout>

<OptionTable
  options={[
    [
      'SAML_ENTRY_POINT',
      'string',
      'The SAML identity provider (IdP) entry point URL.',
      'SAML_ENTRY_POINT=',
    ],
    ['SAML_ISSUER', 'string', 'The SAML service provider (SP) entity ID.', 'SAML_ISSUER='],
    [
      'SAML_CERT',
      'string',
      'The SAML signing certificate, provided as a file path or a one-line PEM string.',
      'SAML_CERT=',
    ],
    [
      'SAML_CALLBACK_URL',
      'string',
      'The callback URL for SAML authentication.',
      'SAML_CALLBACK_URL=/oauth/saml/callback',
    ],
    [
      'SAML_SESSION_SECRET',
      'string',
      'The secret for SAML session storage.',
      'SAML_SESSION_SECRET=',
    ],
    [
      'SAML_NAME_ID_FORMAT',
      'string',
      'Stable NameID format requested from the IdP. Persistent identifiers are recommended; transient identifiers are rejected.',
      '# SAML_NAME_ID_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
    ],
    [
      'SAML_IDP_ISSUER',
      'string',
      'Expected IdP entity ID. When set, assertions with a missing or different issuer are rejected.',
      'SAML_IDP_ISSUER=',
    ],
    [
      'SAML_EMAIL_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the user email. (default: email)',
      'SAML_EMAIL_CLAIM=',
    ],
    [
      'SAML_USERNAME_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the username. (default: username)',
      'SAML_USERNAME_CLAIM=',
    ],
    [
      'SAML_GIVEN_NAME_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the given name. (default: given_name)',
      'SAML_GIVEN_NAME_CLAIM=',
    ],
    [
      'SAML_FAMILY_NAME_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the family name. (default: family_name)',
      'SAML_FAMILY_NAME_CLAIM=',
    ],
    [
      'SAML_PICTURE_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the profile picture URL. (default: picture)',
      'SAML_PICTURE_CLAIM=',
    ],
    [
      'SAML_NAME_CLAIM',
      'string',
      '<Optional>: The attribute in the SAML assertion containing the full name.',
      'SAML_NAME_CLAIM=',
    ],
    [
      'SAML_BUTTON_LABEL',
      'string',
      '<Optional>: The label for the SAML login button.',
      'SAML_BUTTON_LABEL=',
    ],
    [
      'SAML_IMAGE_URL',
      'string',
      '<Optional>: The URL of the SAML login button image.',
      'SAML_IMAGE_URL=',
    ],
    [
      'SAML_USE_AUTHN_RESPONSE_SIGNED',
      'boolean',
      '<Optional>: If "true", signs the entire SAML Response. Otherwise, only the Assertion is signed (default).',
      'SAML_USE_AUTHN_RESPONSE_SIGNED=',
    ],
  ]}
/>

#### LDAP/AD Authentication

For more information: **[LDAP/AD Authentication](/docs/configuration/authentication/ldap)**

<OptionTable
  options={[
    ['LDAP_URL', 'string', 'LDAP server URL.', 'LDAP_URL=ldap://localhost:389'],
    ['LDAP_BIND_DN', 'string', 'Bind DN', 'LDAP_BIND_DN=cn=root'],
    ['LDAP_BIND_CREDENTIALS', 'string', 'Password for bindDN', 'LDAP_BIND_CREDENTIALS=password'],
    [
      'LDAP_USER_SEARCH_BASE',
      'string',
      'LDAP user search base',
      'LDAP_USER_SEARCH_BASE=o=users,o=example.com',
    ],
    ['LDAP_SEARCH_FILTER', 'string', 'LDAP search filter', 'LDAP_SEARCH_FILTER=mail={{username}}'],
    [
      'LDAP_CA_CERT_PATH',
      'string',
      'CA certificate path.',
      'LDAP_CA_CERT_PATH=/path/to/root_ca_cert.crt',
    ],
    [
      'LDAP_TLS_REJECT_UNAUTHORIZED',
      'string',
      'LDAP TLS verification',
      'LDAP_TLS_REJECT_UNAUTHORIZED=true',
    ],
    [
      'LDAP_STARTTLS',
      'string',
      'Enable LDAP StartTLS for upgrading the connection to TLS. Set to true to enable this feature.',
      'LDAP_STARTTLS=true',
    ],
    [
      'LDAP_LOGIN_USES_USERNAME',
      'boolean',
      'Use username instead of email for LDAP login.',
      '# LDAP_LOGIN_USES_USERNAME=true',
    ],
    [
      'LDAP_ID',
      'string',
      'LDAP attribute for unique user ID. Default: uid or sAMAccountName, mail.',
      '# LDAP_ID=uid',
    ],
    [
      'LDAP_USERNAME',
      'string',
      'LDAP attribute for username. Default: givenName or mail.',
      '# LDAP_USERNAME=givenName',
    ],
    [
      'LDAP_EMAIL',
      'string',
      'LDAP attribute for email. Default: mail.',
      '# LDAP_EMAIL=userPrincipalName',
    ],
    [
      'LDAP_FULL_NAME',
      'string',
      'LDAP attribute(s) for full name. Can be comma-separated. Default: givenName + surname.',
      '# LDAP_FULL_NAME=givenName,surname',
    ],
  ]}
/>

### Password Reset

Email is used for account verification and password reset. LibreChat supports both Mailgun API and traditional SMTP services. See: **[Email setup](/docs/configuration/authentication/email)**

**Important Note**: You must configure either Mailgun (recommended for servers that block SMTP) or SMTP for email to work.

> **Warning**: Failing to set valid values for either Mailgun or SMTP will result in LibreChat using the unsecured password reset!

#### Mailgun Configuration (Recommended)

Mailgun is particularly useful for deployments on servers that block SMTP ports. When both `MAILGUN_API_KEY` and `MAILGUN_DOMAIN` are set, LibreChat will use Mailgun instead of SMTP.

<OptionTable
  options={[
    [
      'MAILGUN_API_KEY',
      'string',
      'Your Mailgun API key (required for Mailgun).',
      'MAILGUN_API_KEY=',
    ],
    [
      'MAILGUN_DOMAIN',
      'string',
      'Your Mailgun domain (required for Mailgun).',
      'MAILGUN_DOMAIN=mg.yourdomain.com',
    ],
    [
      'MAILGUN_HOST',
      'string',
      'Custom Mailgun API host (optional). Use https://api.eu.mailgun.net for EU region.',
      'MAILGUN_HOST=https://api.mailgun.net',
    ],
    ['EMAIL_FROM', 'string', 'From email address. Required.', 'EMAIL_FROM=noreply@librechat.ai'],
    [
      'EMAIL_FROM_NAME',
      'string',
      'From name (defaults to APP_TITLE if not set).',
      'EMAIL_FROM_NAME=',
    ],
  ]}
/>

#### SMTP Configuration

If Mailgun is not configured, LibreChat will fall back to SMTP settings.

> **Warning**: If using `EMAIL_SERVICE`, **do NOT** set the extended connection parameters:
> HOST, PORT, ENCRYPTION, ENCRYPTION_HOSTNAME, ALLOW_SELFSIGNED.

See: **[nodemailer well-known-services](https://nodemailer.com/smtp/well-known-services)**

<OptionTable
  options={[
    ['EMAIL_SERVICE', 'string', 'Email service (e.g., Gmail, Outlook).', 'EMAIL_SERVICE='],
    ['EMAIL_HOST', 'string', 'Mail server host.', 'EMAIL_HOST='],
    ['EMAIL_PORT', 'number', 'Mail server port.', 'EMAIL_PORT=25'],
    ['EMAIL_ENCRYPTION', 'string', 'Encryption method (starttls, tls, etc.).', 'EMAIL_ENCRYPTION='],
    [
      'EMAIL_ENCRYPTION_HOSTNAME',
      'string',
      'Hostname for encryption.',
      'EMAIL_ENCRYPTION_HOSTNAME=',
    ],
    [
      'EMAIL_ALLOW_SELFSIGNED',
      'boolean',
      'Allow self-signed certificates.',
      'EMAIL_ALLOW_SELFSIGNED=',
    ],
    ['EMAIL_USERNAME', 'string', 'Username for authentication.', 'EMAIL_USERNAME='],
    ['EMAIL_PASSWORD', 'string', 'Password for authentication.', 'EMAIL_PASSWORD='],
    ['EMAIL_FROM_NAME', 'string', 'From name.', 'EMAIL_FROM_NAME='],
    ['EMAIL_FROM', 'string', 'From email address. Required.', 'EMAIL_FROM=noreply@librechat.ai'],
  ]}
/>

### Firebase CDN

See: **[Firebase CDN Configuration](/docs/configuration/cdn/firebase)**

<Callout type="warning" title="Important">
  - If you are using Firebase as your file storage strategy, set `fileStrategy` or `fileStrategies`
  to `firebase` in your `librechat.yaml` configuration file. For more information on configuring the
  `librechat.yaml` file, please refer to the YAML Configuration Guide: [Custom Endpoints &
  Configuration](/docs/configuration/librechat_yaml)
</Callout>

<OptionTable
  options={[
    ['FIREBASE_API_KEY', 'string', 'The API key for your Firebase project.', 'FIREBASE_API_KEY='],
    [
      'FIREBASE_AUTH_DOMAIN',
      'string',
      'The Firebase Auth domain for your project.',
      'FIREBASE_AUTH_DOMAIN=',
    ],
    ['FIREBASE_PROJECT_ID', 'string', 'The ID of your Firebase project.', 'FIREBASE_PROJECT_ID='],
    [
      'FIREBASE_STORAGE_BUCKET',
      'string',
      'The Firebase Storage bucket for your project.',
      'FIREBASE_STORAGE_BUCKET=',
    ],
    [
      'FIREBASE_MESSAGING_SENDER_ID',
      'string',
      'The Firebase Cloud Messaging sender ID.',
      'FIREBASE_MESSAGING_SENDER_ID=',
    ],
    ['FIREBASE_APP_ID', 'string', 'The Firebase App ID for your project.', 'FIREBASE_APP_ID='],
  ]}
/>

### Amazon S3 and CloudFront

See: **[Amazon S3 Configuration](/docs/configuration/cdn/s3)** and **[CloudFront with S3](/docs/configuration/cdn/cloudfront)**

<Callout type="warning" title="Important">
  If you are using S3 as your file storage strategy, set `fileStrategy` or `fileStrategies` in your
  `librechat.yaml` configuration file. If you use CloudFront, S3 is still required as the storage
  origin.
</Callout>

<OptionTable
  options={[
    [
      'AWS_ACCESS_KEY_ID',
      'string',
      'Your IAM user access key ID. Optional if using IRSA.',
      'AWS_ACCESS_KEY_ID=your_access_key_id',
    ],
    [
      'AWS_SECRET_ACCESS_KEY',
      'string',
      'Your IAM user secret access key. Optional if using IRSA.',
      'AWS_SECRET_ACCESS_KEY=your_secret_access_key',
    ],
    [
      'AWS_REGION',
      'string',
      'The AWS region where your S3 bucket is located.',
      'AWS_REGION=us-east-1',
    ],
    [
      'AWS_BUCKET_NAME',
      'string',
      'The name of the S3 bucket for file storage.',
      'AWS_BUCKET_NAME=your_bucket_name',
    ],
    [
      'AWS_ENDPOINT_URL',
      'string',
      'Custom AWS endpoint URL (optional). For S3-compatible services. Include the URL scheme, such as https://a7g8.da.idrivee2-32.com.',
      '# AWS_ENDPOINT_URL=https://your_endpoint_url',
    ],
    [
      'AWS_FORCE_PATH_STYLE',
      'boolean',
      'Set to true for S3-compatible providers that require path-style URLs (e.g. MinIO, Hetzner, Backblaze B2). Not needed for AWS S3. Default: false.',
      '# AWS_FORCE_PATH_STYLE=false',
    ],
    [
      'CLOUDFRONT_KEY_PAIR_ID',
      'string',
      'CloudFront public key pair ID. Required for signed cookies and signed CloudFront download URLs.',
      '# CLOUDFRONT_KEY_PAIR_ID=K1234567890ABC',
    ],
    [
      'CLOUDFRONT_PRIVATE_KEY',
      'string',
      'CloudFront private key PEM. Required for signed cookies and signed CloudFront download URLs. Preserve PEM newlines when injecting this secret.',
      '# CLOUDFRONT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----"',
    ],
  ]}
/>

> **Note:** For Kubernetes deployments (e.g., on EKS), you can use IRSA (IAM Roles for Service Accounts) instead of providing explicit credentials. In that case, only `AWS_REGION` and `AWS_BUCKET_NAME` are required.

### Azure Blob Storage CDN

See: **[Azure Blob Storage CDN Configuration](/docs/configuration/cdn/azure)**

<Callout type="warning" title="Important">
  If you are using Azure Blob Storage as your file storage strategy, set `fileStrategy` or
  `fileStrategies` to `azure_blob` in your `librechat.yaml` configuration file.
</Callout>

<OptionTable
  options={[
    [
      'AZURE_STORAGE_CONNECTION_STRING',
      'string',
      'Azure Blob Storage connection string. Use this OR AZURE_STORAGE_ACCOUNT_NAME for Managed Identity.',
      'AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...',
    ],
    [
      'AZURE_STORAGE_ACCOUNT_NAME',
      'string',
      'Azure Storage account name. Use for Managed Identity authentication (do not set connection string).',
      '# AZURE_STORAGE_ACCOUNT_NAME=yourAccountName',
    ],
    [
      'AZURE_STORAGE_PUBLIC_ACCESS',
      'boolean',
      'Enable public access for blobs. Default: false.',
      'AZURE_STORAGE_PUBLIC_ACCESS=false',
    ],
    [
      'AZURE_CONTAINER_NAME',
      'string',
      'Container name for file storage. Default: files.',
      'AZURE_CONTAINER_NAME=files',
    ],
  ]}
/>

> **Note:** Use either `AZURE_STORAGE_CONNECTION_STRING` (Option A) or `AZURE_STORAGE_ACCOUNT_NAME` with Managed Identity (Option B), not both.

### UI

#### Help and FAQ Button

<OptionTable
  options={[
    [
      'HELP_AND_FAQ_URL',
      'string',
      'Help and FAQ URL. If empty or commented, the button is enabled. To disable the Help and FAQ button, set to "/".',
      'HELP_AND_FAQ_URL=https://librechat.ai',
    ],
  ]}
/>

**Behaviour:**

Sets the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) headers for static files. These configurations only trigger when the `NODE_ENV` is set to `production`.

Properly setting cache headers is crucial for optimizing the performance and efficiency of your web application. By controlling how long browsers and CDNs store copies of your static files, you can significantly reduce server load, decrease page load times, and improve the overall user experience.

- Uncomment `STATIC_CACHE_MAX_AGE` to change the `max-age` for static files. By default this is set to 4 weeks.
- Uncomment `STATIC_CACHE_S_MAX_AGE` to change the `s-maxage` for static files. By default this is set to 1 week.
  - This is for the _shared cache_, which is used by CDNs and proxies.

#### App Title and Footer

<OptionTable
  options={[
    ['APP_TITLE', 'string', 'App title.', 'APP_TITLE=LibreChat'],
    ['CUSTOM_FOOTER', 'string', 'Custom footer.', '# CUSTOM_FOOTER="My custom footer"'],
    [
      'TEMP_CHAT_RETENTION_HOURS',
      'number',
      '**Deprecated:** Use `interface.temporaryChatRetention` in librechat.yaml instead. Hours to retain temporary chats. Default: 720 (30 days).',
      '# TEMP_CHAT_RETENTION_HOURS=168',
    ],
  ]}
/>

**Behaviour:**

- Uncomment `CUSTOM_FOOTER` to add a custom footer.
- Uncomment and leave `CUSTOM_FOOTER` empty to remove the footer.
- You can now add one or more links in the CUSTOM_FOOTER value using the following format: `[Anchor text](URL)`. Each link should be delineated with a pipe (`|`).

> **Markdown example:** `CUSTOM_FOOTER=[Link 1](http://example1.com) | [Link 2](http://example2.com)`

#### Birthday Hat

<OptionTable
  options={[
    ['SHOW_BIRTHDAY_ICON', 'boolean', 'Show the birthday hat icon.', '# SHOW_BIRTHDAY_ICON=true'],
  ]}
/>

**Behaviour:**

- The birthday hat icon will show automatically on February 11th (LibreChat's birthday).
- Set `SHOW_BIRTHDAY_ICON` to `false` to disable the birthday hat.
- Set `SHOW_BIRTHDAY_ICON` to `true` to enable the birthday hat all the time.

### Analytics

#### Google Tag Manager

LibreChat supports Google Tag Manager for analytics. You will need a Google Tag Manager ID to enable it in LibreChat. Follow [this guide](https://support.google.com/tagmanager/answer/9442095?sjid=10155093630524971297-EU) to generate a Google Tag Manager ID and configure Google Analytics. Then set the `ANALYTICS_GTM_ID` environment variable to your Google Tag Manager ID.

**Note:** If `ANALYTICS_GTM_ID` is not set, Google Tag Manager will not be enabled. If it is set incorrectly, you will see failing requests to `gtm.js`

<OptionTable
  options={[['ANALYTICS_GTM_ID', 'string', 'Google Tag Manager ID.', 'ANALYTICS_GTM_ID=']]}
/>

#### Conversation Import

Configure limits for conversation file imports to prevent memory issues.

<OptionTable
  options={[
    [
      'CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES',
      'number',
      'Maximum file size in bytes for conversation imports. Default: 0 (no limit enforced). Example: 262144000 (250 MiB).',
      '# CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES=262144000',
    ],
  ]}
/>

#### Inline File Previews

Control how large generated files can be before LibreChat skips inline preview extraction and leaves them download-only.

<OptionTable
  options={[
    [
      'FILE_PREVIEW_MAX_EXTRACT_BYTES',
      'number',
      'Maximum source file size in bytes for code-execution artifact inline previews, including DOCX, XLSX, CSV, PPTX, POTX, text, and PDF-like files. Default: 2097152 (2 MiB). Rendered HTML previews are still capped separately, so very rich files may skip preview even below this value.',
      '# FILE_PREVIEW_MAX_EXTRACT_BYTES=2097152',
    ],
  ]}
/>

### MCP (Model Context Protocol)

Configure Model Context Protocol settings for enhanced server management and OAuth support.

#### MCP Server Configuration

<OptionTable
  options={[
    [
      'MCP_OAUTH_ON_AUTH_ERROR',
      'boolean',
      'Treat 401/403 responses as OAuth requirement when no oauth metadata found.',
      'MCP_OAUTH_ON_AUTH_ERROR=true',
    ],
    [
      'MCP_OAUTH_DETECTION_TIMEOUT',
      'number',
      'Timeout for OAuth detection requests in milliseconds.',
      'MCP_OAUTH_DETECTION_TIMEOUT=5000',
    ],
    [
      'MCP_OAUTH_HANDLING_TIMEOUT',
      'number',
      'How long LibreChat waits for a user to complete an MCP OAuth flow before timing out. Default: 600000 ms (10 minutes).',
      'MCP_OAUTH_HANDLING_TIMEOUT=600000',
    ],
    [
      'MCP_OAUTH_FLOW_TTL',
      'number',
      'How long MCP OAuth flow state is retained. LibreChat clamps this above MCP_OAUTH_HANDLING_TIMEOUT so callbacks near the deadline can still complete. Default: 900000 ms (15 minutes).',
      'MCP_OAUTH_FLOW_TTL=900000',
    ],
    [
      'MCP_CONNECTION_CHECK_TTL',
      'number',
      'Cache connection status checks for this many milliseconds to avoid expensive verification.',
      'MCP_CONNECTION_CHECK_TTL=30000',
    ],
    [
      'MCP_TOOLS_LIST_MAX_PAGES',
      'number',
      'Maximum number of tools/list pages to request when an MCP server paginates its tool list (cursor pagination). Bounds the pagination loop so a misbehaving server cannot stall tool discovery. Clamped to a minimum of 1. Default: 50.',
      'MCP_TOOLS_LIST_MAX_PAGES=50',
    ],
    [
      'MCP_SKIP_CODE_CHALLENGE_CHECK',
      'boolean',
      'Skip code challenge method validation. When set to true, forces S256 code challenge even if not advertised in .well-known/openid-configuration',
      'MCP_SKIP_CODE_CHALLENGE_CHECK=false',
    ],
    [
      'MCP_STREAMABLE_HTTP_MAX_RESPONSE_BYTES',
      'number',
      'Maximum bytes allowed in a non-GET streamable HTTP MCP response before rejecting it. Set to 0 to disable. Default: 16777216 (16 MiB).',
      '# MCP_STREAMABLE_HTTP_MAX_RESPONSE_BYTES=16777216',
    ],
    [
      'MCP_STREAMABLE_HTTP_MAX_LINE_BYTES',
      'number',
      'Maximum bytes allowed in one SSE line for non-GET streamable HTTP MCP responses. Set to 0 to disable. Default: 5242880 (5 MiB).',
      '# MCP_STREAMABLE_HTTP_MAX_LINE_BYTES=5242880',
    ],
  ]}
/>

### Other

#### Redis

Redis provides significant performance improvements and enables horizontal scaling capabilities for LibreChat.

**Note:** Redis support is experimental, and you may encounter some problems when using it.

**Important:** If using Redis, you should flush the cache after changing any LibreChat settings.

For detailed configuration and examples, see: **[Redis Configuration Guide](/docs/configuration/redis)**

<OptionTable
  options={[
    [
      'USE_REDIS',
      'boolean',
      'Enable Redis for caching and session storage. When true, REDIS_URI must be provided.',
      'USE_REDIS=true',
    ],
    [
      'USE_REDIS_STREAMS',
      'boolean',
      'Enable Redis for resumable LLM streams. Defaults to USE_REDIS value if not set. Set to false to use in-memory storage for streams.',
      '# USE_REDIS_STREAMS=true',
    ],
    [
      'STREAM_DELTA_COALESCE_MS',
      'integer',
      'Batch Redis stream delta publications within this many milliseconds. Unset or 0 disables batching; 25 is recommended and values are capped at 1000.',
      '# STREAM_DELTA_COALESCE_MS=25',
    ],
    [
      'REDIS_URI',
      'string',
      'Redis connection URI. For single instance: `redis://host:port`. For cluster: comma-separated URIs.',
      'REDIS_URI=redis://127.0.0.1:6379',
    ],
    [
      'USE_REDIS_CLUSTER',
      'boolean',
      'Enable Redis cluster mode when using a single URI',
      '# USE_REDIS_CLUSTER="true"',
    ],
    [
      'REDIS_CLUSTER_SAFE_DELETE',
      'boolean',
      'Delete Redis cache keys individually to avoid CROSSSLOT errors on single-endpoint managed Redis services that shard keys internally.',
      '# REDIS_CLUSTER_SAFE_DELETE=true',
    ],
    [
      'REDIS_USERNAME',
      'string',
      'Redis username for authentication. Overrides username in URI if both provided.',
      '# REDIS_USERNAME=your_redis_username',
    ],
    [
      'REDIS_PASSWORD',
      'string',
      'Redis password for authentication. Overrides password in URI if both provided.',
      '# REDIS_PASSWORD=your_redis_password',
    ],
    [
      'REDIS_CA',
      'string',
      'Path to CA certificate for TLS verification when using rediss:// protocol.',
      '# REDIS_CA=/path/to/ca-cert.pem',
    ],
    [
      'REDIS_KEY_PREFIX',
      'string',
      'Static prefix for all Redis keys to prevent cross-deployment contamination.',
      '# REDIS_KEY_PREFIX=librechat-prod-v2',
    ],
    [
      'REDIS_KEY_PREFIX_VAR',
      'string',
      'Environment variable name containing dynamic prefix (e.g., K_REVISION for Cloud Run). Cannot be used with REDIS_KEY_PREFIX.',
      '# REDIS_KEY_PREFIX_VAR=K_REVISION',
    ],
    [
      'REDIS_MAX_LISTENERS',
      'number',
      'Maximum event listeners per Redis client. Prevents memory leaks. Default: 40.',
      '# REDIS_MAX_LISTENERS=40',
    ],
    [
      'REDIS_READONLY_RECOVERY_INTERVAL',
      'number',
      'Minimum milliseconds between forced Keyv reconnect attempts after READONLY replies during standalone or Sentinel failover. Default: 5000.',
      '# REDIS_READONLY_RECOVERY_INTERVAL=5000',
    ],
    [
      'REDIS_PING_INTERVAL',
      'number',
      'Ping interval in seconds to maintain connections. Default: 0 (disabled). Only set if experiencing timeouts.',
      '# REDIS_PING_INTERVAL=300',
    ],
    [
      'FORCED_IN_MEMORY_CACHE_NAMESPACES',
      'string',
      'Comma-separated cache keys to force in-memory storage even when Redis is enabled.',
      '# FORCED_IN_MEMORY_CACHE_NAMESPACES=ROLES,MESSAGES',
    ],
    [
      'AUTH_USER_CACHE_MODE',
      'string',
      'Set exactly to `on` to cache authenticated user documents during request bursts. Requires Redis and a Redis-backed AUTH_USER_DOC namespace. Default: off.',
      '# AUTH_USER_CACHE_MODE=off',
    ],
    [
      'USER_PRINCIPALS_CACHE_TTL_MS',
      'number',
      'TTL in milliseconds for cached group memberships used in ACL checks. Set to 0 to disable. Default: 300000.',
      '# USER_PRINCIPALS_CACHE_TTL_MS=300000',
    ],
    [
      'USER_PRINCIPALS_LOCK_TTL_MS',
      'number',
      'Redis lock TTL in milliseconds for cross-container principal-cache builds. Set to 0 to disable build locking. Default: 5000.',
      '# USER_PRINCIPALS_LOCK_TTL_MS=5000',
    ],
    [
      'USER_PRINCIPALS_LOCK_WAIT_MS',
      'number',
      'Maximum time to wait for another container to fill the principal cache before reading the database directly. Defaults to USER_PRINCIPALS_LOCK_TTL_MS.',
      '# USER_PRINCIPALS_LOCK_WAIT_MS=5000',
    ],
    [
      'REDIS_USE_ALTERNATIVE_DNS_LOOKUP',
      'boolean',
      'Enable alternate dnsLookup for TLS connections with AWS Elasticache. Required for Elasticache clusters with TLS.',
      '# REDIS_USE_ALTERNATIVE_DNS_LOOKUP=true',
    ],
  ]}
/>

Notes:

- When `USE_REDIS=true`, you must provide `REDIS_URI` or the application will throw an error.
- Current LibreChat clients negotiate generation protocol v2 automatically. The retired `GENERATION_PROTOCOL_VERSION` variable is no longer read. See [Generation Protocol Compatibility](/docs/configuration/redis#generation-protocol-compatibility) before mixing release versions or rolling back.
- Enable `STREAM_DELTA_COALESCE_MS` only after every replica supports batch frames. Older subscribers drop coalesced frames. See [Stream Delta Coalescing](/docs/configuration/redis#stream-delta-coalescing).
- For Redis Cluster mode, provide multiple URIs: `redis://node1:7001,redis://node2:7002,redis://node3:7003` (cluster mode is auto-detected).
- For single-endpoint managed Redis services that shard keys internally, keep `USE_REDIS_CLUSTER=false` and set `REDIS_CLUSTER_SAFE_DELETE=true` if cache clears fail with `CROSSSLOT` errors.
- `REDIS_READONLY_RECOVERY_INTERVAL` debounces Keyv reconnect attempts after a failover leaves an open socket attached to a demoted read-only replica. Redis Cluster clients use their native topology handling instead.
- Use `rediss://` protocol for TLS connections and set `REDIS_CA` if your CA is not publicly trusted.
- `REDIS_KEY_PREFIX_VAR` and `REDIS_KEY_PREFIX` are mutually exclusive.
- **AWS Elasticache with TLS**: Elasticache may need to use an alternate dnsLookup for TLS connections. Set `REDIS_USE_ALTERNATIVE_DNS_LOOKUP=true` if using Elasticache with TLS. See [ioredis documentation](https://www.npmjs.com/package/ioredis) for more details.

#### Leader Election

Configure distributed leader election for multi-instance deployments with Redis. Leader election ensures only one instance performs certain operations like scheduled tasks.

<OptionTable
  options={[
    [
      'LEADER_LEASE_DURATION',
      'number',
      'Duration in seconds that the leader lease is valid before it expires. Default: 25.',
      'LEADER_LEASE_DURATION=25',
    ],
    [
      'LEADER_RENEW_INTERVAL',
      'number',
      'Interval in seconds at which the leader renews its lease. Default: 10.',
      'LEADER_RENEW_INTERVAL=10',
    ],
    [
      'LEADER_RENEW_ATTEMPTS',
      'number',
      'Maximum number of retry attempts when renewing the lease fails. Default: 3.',
      'LEADER_RENEW_ATTEMPTS=3',
    ],
    [
      'LEADER_RENEW_RETRY_DELAY',
      'number',
      'Delay in seconds between retry attempts when renewing the lease. Default: 0.5.',
      'LEADER_RENEW_RETRY_DELAY=0.5',
    ],
  ]}
/>

Notes:

- Leader election requires Redis to be enabled (`USE_REDIS=true`).
- These settings are only relevant for multi-instance deployments.
- The leader lease must be renewed before expiration to maintain leadership.
- If lease renewal fails after max attempts, the instance will relinquish leadership.


# HTTP Security Headers (https://www.librechat.ai/docs/configuration/security_headers)

LibreChat sends a baseline set of HTTP security headers on every response, including health endpoints. A nonce-based Content Security Policy (CSP) is available separately and is disabled by default so existing integrations can be audited before enforcement.

## Baseline Headers

Baseline headers are enabled unless `SECURITY_HEADERS=false`. That variable is the global kill switch: it disables both the baseline headers and CSP.

| Variable | Default | Behavior |
| --- | --- | --- |
| `SECURITY_HEADERS` | `true` | Enables all baseline headers. `false` also disables CSP. |
| `HSTS_ENABLED` | `true` | Sends `Strict-Transport-Security`. Browsers honor it only over HTTPS. |
| `HSTS_MAX_AGE` | `31536000` | HSTS lifetime in seconds. Must be a non-negative integer. |
| `HSTS_INCLUDE_SUBDOMAINS` | `false` | Applies HSTS to every subdomain. Enable only when all subdomains use HTTPS. |
| `HSTS_PRELOAD` | `false` | Adds the HSTS `preload` token. |
| `X_FRAME_OPTIONS` | `SAMEORIGIN` | Accepts `SAMEORIGIN`, `DENY`, or `off`. |
| `REFERRER_POLICY` | `no-referrer` | Sets `Referrer-Policy`; use `off` to omit it. |
| `CROSS_ORIGIN_OPENER_POLICY` | `same-origin` | Sets `Cross-Origin-Opener-Policy`; use `off` to omit it. |
| `CROSS_ORIGIN_RESOURCE_POLICY` | `same-origin` | Sets `Cross-Origin-Resource-Policy`; use `off` to omit it. |

Helmet also sends `X-Content-Type-Options: nosniff`. Invalid values are logged and fall back to the documented defaults.

Accepted opener policies are `same-origin`, `same-origin-allow-popups`, `noopener-allow-popups`, and `unsafe-none`. Accepted resource policies are `same-origin`, `same-site`, and `cross-origin`. Accepted referrer policies are `no-referrer`, `no-referrer-when-downgrade`, `same-origin`, `origin`, `strict-origin`, `origin-when-cross-origin`, `strict-origin-when-cross-origin`, and `unsafe-url`.

```bash filename=".env"
SECURITY_HEADERS=true
HSTS_ENABLED=true
HSTS_MAX_AGE=31536000
HSTS_INCLUDE_SUBDOMAINS=false
HSTS_PRELOAD=false
X_FRAME_OPTIONS=SAMEORIGIN
REFERRER_POLICY=no-referrer
CROSS_ORIGIN_OPENER_POLICY=same-origin
CROSS_ORIGIN_RESOURCE_POLICY=same-origin
```

If an upstream proxy also writes these headers, keep one authoritative policy and verify the final response seen by the browser.

## Content Security Policy

CSP is opt-in. LibreChat creates a fresh nonce for every SPA response and applies it to executable scripts and module-preload links in the shell.

<Callout type="warning" title="Start in report-only mode">
  Enable CSP with `CSP_REPORT_ONLY=true`, collect violations from your real deployment, add only the required sources, and enforce only after the app, authentication, storage, telemetry, and embedded-content flows are clean.
</Callout>

```bash filename=".env"
CSP_ENABLED=true
CSP_REPORT_ONLY=true
CSP_REPORT_URI=https://reports.example.com/csp

# Examples for deployment-specific services
CSP_CONNECT_SRC_EXTRA="https://telemetry.example.com wss://stream.example.com"
CSP_FRAME_SRC_EXTRA=https://tenant.sharepoint.com
CSP_IMG_SRC_EXTRA=https://cdn.example.com
```

`CSP_REPORT_ONLY` defaults to `true`. Only a recognized false value switches to enforcement; an unrecognized value is logged and remains report-only. While CSP is enabled, LibreChat forces the SPA shell to `Cache-Control: no-store` and ignores `INDEX_CACHE_CONTROL`, `INDEX_PRAGMA`, and `INDEX_EXPIRES` for that response so a nonce cannot be reused from cache.

### CSP Variables

| Variable | Default | Behavior |
| --- | --- | --- |
| `CSP_ENABLED` | `false` | Enables nonce-based CSP for the SPA shell. |
| `CSP_REPORT_ONLY` | `true` | Sends `Content-Security-Policy-Report-Only`; set `false` to enforce. |
| `CSP_REPORT_URI` | empty | Adds the legacy `report-uri` directive. |
| `CSP_ALLOW_WASM` | `true` | Allows WebAssembly compilation used by HEIC conversion. |
| `CSP_ALLOW_DATA_WORKERS` | `true` | Allows `data:` workers used by Monaco's loader. |
| `CSP_FRAME_ANCESTORS` | `'self'` | Replaces the complete `frame-ancestors` source list. |
| `CSP_ADDITIONAL_DIRECTIVES` | empty | Adds semicolon-separated raw directives. |

These variables append sources to the matching built-in directive:

- `CSP_DEFAULT_SRC_EXTRA`
- `CSP_SCRIPT_SRC_EXTRA`
- `CSP_STYLE_SRC_EXTRA`
- `CSP_IMG_SRC_EXTRA`
- `CSP_FONT_SRC_EXTRA`
- `CSP_CONNECT_SRC_EXTRA`
- `CSP_MEDIA_SRC_EXTRA`
- `CSP_FRAME_SRC_EXTRA`
- `CSP_WORKER_SRC_EXTRA`
- `CSP_FORM_ACTION_EXTRA`

Source lists can be comma- or space-separated. They append rather than replace LibreChat's defaults. `CSP_FRAME_ANCESTORS` is the exception because it intentionally replaces the default `'self'` value.

The default script policy uses `'strict-dynamic'`. Setting `CSP_SCRIPT_SRC_EXTRA` removes `'strict-dynamic'` so the listed script hosts can take effect. List only origins you trust to execute code in LibreChat.

If another origin must frame LibreChat, enforce `frame-ancestors` before removing `X-Frame-Options`:

```bash filename=".env"
CSP_ENABLED=true
CSP_REPORT_ONLY=false
X_FRAME_OPTIONS=off
CSP_FRAME_ANCESTORS="'self' https://portal.example.com"
```

Do not set `X_FRAME_OPTIONS=off` while CSP is disabled or report-only. A report-only policy records violations but does not restrict framing, so removing `X-Frame-Options` first would allow any origin to frame the deployment. Browsers without `frame-ancestors` support will not enforce a framing restriction in this cross-origin setup.

See the [environment variable reference](/docs/configuration/dotenv#security-headers-and-content-security-policy) for where these settings live in `.env`.


# Custom Config (https://www.librechat.ai/docs/configuration/librechat_yaml)

## What is librechat.yaml?

The `librechat.yaml` file is LibreChat's main configuration file for custom AI endpoints, model settings, interface options, and advanced features like MCP servers and agents. It is optional -- LibreChat works with sensible defaults if the file does not exist.

Follow the steps below to create the file, mount it for your deployment type, and verify it works.

<Callout type="info" title="If you only remember one thing">

For Docker installs, editing `librechat.yaml` is not enough. The file must exist in the project root, be mounted into the API container, and LibreChat must be restarted before changes appear in the UI.

</Callout>

<Callout type="info" title="Prefer a UI? Use the Admin Panel">

The [**LibreChat Admin Panel**](/docs/features/admin_panel) manages this same configuration from a browser -- including per-role and per-group overrides that take effect at login without restarting LibreChat. It ships with the official Docker Compose stacks. Use `librechat.yaml` for file-driven or bootstrap setup, and the admin panel for ongoing management.

</Callout>

## Setup

<Steps>
  <Step>

### Locate or Create the File

Create a new `librechat.yaml` in your project root (the same directory as your `.env` file):

```bash
touch librechat.yaml
```

You can also copy the [example config](/docs/configuration/librechat_yaml/example) as a starting point:

```bash
cp librechat.example.yaml librechat.yaml
```

<Callout type="info" title="Alternative File Path">

You can set a custom file path using the `CONFIG_PATH` environment variable:

```bash filename=".env"
CONFIG_PATH="/alternative/path/to/librechat.yaml"
```

</Callout>

  </Step>
  <Step>

### Mount the Config File

<Tabs items={['Docker', 'Local']}>
  <Tabs.Tab>

Docker needs a volume mount to access your `librechat.yaml` file inside the container.

**Copy the example override file:**

```bash
cp docker-compose.override.yml.example docker-compose.override.yml
```

**Edit `docker-compose.override.yml`** and ensure the librechat.yaml volume mount is uncommented:

```yaml filename="docker-compose.override.yml"
services:
  api:
    volumes:
      - type: bind
        source: ./librechat.yaml
        target: /app/librechat.yaml
```

This uses the [docker-compose.override.yml](/docs/configuration/docker_override) pattern -- Docker Compose automatically merges it with the main `docker-compose.yml`, so your customizations survive updates.

  </Tabs.Tab>
  <Tabs.Tab>

Place `librechat.yaml` in the project root directory (the same directory as your `.env` file). No additional mounting is needed for local installations.

  </Tabs.Tab>
</Tabs>

  </Step>
  <Step>

### Restart LibreChat

<Tabs items={['Docker', 'Local']}>
  <Tabs.Tab>

```bash
docker compose down && docker compose up -d
```

  </Tabs.Tab>
  <Tabs.Tab>

Stop the running process (Ctrl+C) and restart:

```bash
npm run backend
```

  </Tabs.Tab>
</Tabs>

  </Step>
  <Step>

### Verify It Works

Open LibreChat in your browser. If your configuration includes custom endpoints, you should see them in the model selector dropdown.

If the server fails to start, check the logs for validation errors:

```bash
docker compose logs api
```

  </Step>
</Steps>

## Example: Adding OpenRouter

This example walks through adding [OpenRouter](https://openrouter.ai/) as a custom endpoint -- one of the most popular configurations.

**1. Get an API key** from [openrouter.ai/keys](https://openrouter.ai/keys).

**2. Add the key to your `.env` file:**

```bash filename=".env"
OPENROUTER_KEY=sk-or-v1-your-key-here
```

<Callout type="warning" title="Environment Variable Name">

Use `OPENROUTER_KEY`, not `OPENROUTER_API_KEY`. Using `OPENROUTER_API_KEY` will override the OpenAI endpoint to use OpenRouter as well.

</Callout>

**3. Add the endpoint to `librechat.yaml`:**

```yaml filename="librechat.yaml"
version: 1.3.5
cache: true
endpoints:
  custom:
    - name: "OpenRouter"
      apiKey: "${OPENROUTER_KEY}"
      baseURL: "https://openrouter.ai/api/v1"
      models:
        default: ["meta-llama/llama-3-70b-instruct"]
        fetch: true
      titleConvo: true
      titleModel: "meta-llama/llama-3-70b-instruct"
      dropParams: ["stop"]
      modelDisplayLabel: "OpenRouter"
```

**4. Restart LibreChat** (see restart commands above) and select OpenRouter from the model selector.

For the full annotated config file with more endpoint examples, see the [example configuration](/docs/configuration/librechat_yaml/example).

## Reference

For detailed field-level documentation, see the reference pages below.

<Cards num={2}>
  <Cards.Card title="AI Endpoints" href="/docs/configuration/librechat_yaml/ai_endpoints" arrow>
    Compatible AI providers and example endpoint configurations
  </Cards.Card>
  <Cards.Card
    title="Object Structure"
    href="/docs/configuration/librechat_yaml/object_structure/config"
    arrow
  >
    Complete field reference for every librechat.yaml option
  </Cards.Card>
</Cards>

## Troubleshooting

### Change Does Not Show in LibreChat

If you edited `librechat.yaml` and nothing changed in the UI:

1. Confirm the file is in the LibreChat project root unless you set `CONFIG_PATH`.
2. For Docker, confirm the file is mounted in `docker-compose.override.yml`.
3. Restart LibreChat with `docker compose down && docker compose up -d`.
4. Check the API logs with `docker compose logs api`.
5. Validate the file with the [YAML Validator](/toolkit/yaml_checker).

Custom endpoints such as OpenRouter only appear after all three pieces are correct: `.env` contains the key, `librechat.yaml` defines the endpoint, and Docker can read the mounted config file.

### Configuration Validation

<Callout type="error" title="Configuration Validation">

LibreChat exits with an error (exit code 1) if `librechat.yaml` contains validation errors. This fail-fast behavior catches configuration issues early.

To validate your YAML syntax before restarting, use the [YAML Validator](/toolkit/yaml_checker) or [yamlchecker.com](https://yamlchecker.com/).

</Callout>

### Server Exits Immediately on Startup

If your server exits immediately after starting, this is likely a configuration validation error.

**To diagnose:**

1. Check server logs: `docker compose logs api`
2. Validate your YAML syntax with the [YAML Validator](/toolkit/yaml_checker)
3. Common errors: incorrect indentation, missing colons, unknown keys, invalid values

**Temporary workaround** (not recommended for production):

```bash filename=".env"
CONFIG_BYPASS_VALIDATION=true
```

<Callout type="warning" title="Warning">

`CONFIG_BYPASS_VALIDATION=true` causes the server to skip validation and use default configuration. Always fix the validation errors instead.

</Callout>


# Example (https://www.librechat.ai/docs/configuration/librechat_yaml/example)

## Clean Example

<Callout type="example" title="Example" collapsible>

This example config includes all documented endpoints (Except Azure, LiteLLM, MLX, and Ollama, which all require additional configurations)

```yaml filename="librechat.yaml"
version: 1.3.15

cache: true

# Optional deployment-level headers for one self-hosted Langfuse origin or gateway.
# Use environment references for credentials.
# langfuse:
#   headers:
#     CF-Access-Client-Id: '${CF_ACCESS_CLIENT_ID}'
#     CF-Access-Client-Secret: '${CF_ACCESS_CLIENT_SECRET}'

# skillSync:
#   github:
#     enabled: false
#     intervalMinutes: 60
#     runOnStartup: true
#     sources:
#       - id: librechat-skills
#         owner: your-org
#         repo: your-skills-repo
#         ref: main
#         paths:
#           - skills
#         skillDiscoveryDepth: 2
#         token: '${GITHUB_SKILLS_TOKEN}'
#         # credentialKey: production-skills
#         # tenantId: your-tenant-id

interface:
  # MCP Servers UI configuration
  mcpServers:
    placeholder: 'MCP Servers'
    # configureObo: false # Permission to configure MCP On-Behalf-Of token exchange

  # Shared Links permissions
  # sharedLinks:
  #   create: true
  #   share: true
  #   public: false
  #   snapshotFiles: true

  # Experimental Scheduled Chats (disabled when omitted)
  # Multi-replica deployments require USE_REDIS_STREAMS=true. A truly
  # single-process deployment without Redis must set SCHEDULES_SINGLE_PROCESS=true.
  # schedules:
  #   use: true
  #   create: true
  #   maxPerUser: 10
  #   minIntervalMinutes: 60
  #   autoDisableAfterFailures: 5
  #   fireConcurrency: 5
  #   requireProject: false
  #   # projectId: 'project-id' # Pins every schedule to one project and implies requireProject

  # Skills permissions and activation defaults
  # skills:
  #   use: true
  #   create: true
  #   share: false
  #   public: false
  #   defaultActiveOnShare: false

  # Retention mode: "temporary" applies only to temporary chats; "all" applies to all retained data
  # retentionMode: "temporary"
  # retainAgentFiles: false # Keep persistent agent resource files when retentionMode is "all"

  # Privacy policy settings
  privacyPolicy:
    externalUrl: 'https://librechat.ai/privacy-policy'
    openNewTab: true

  # Terms of service
  termsOfService:
    externalUrl: 'https://librechat.ai/tos'
    openNewTab: true

  # Context usage and cost display
  # contextUsage: true
  # contextCost: true
  # currency:
  #   code: EUR
  #   rate: 0.92

  # Show the thumbs up/thumbs down feedback buttons on responses (default: true)
  # feedback: true

  # Default prompt-bar pinned tools for users who have not customized pins
  # defaultPinnedTools:
  #   - artifacts
  #   - execute_code
  #   - mcp

registration:
  socialLogins: ['discord', 'facebook', 'github', 'google', 'openid']

# Source-aware content filters are disabled when omitted. This policy is
# base-config-only and cannot be changed through database overrides.
# filters:
#   messages:
#     pii:
#       action: audit # `block` (default) or `audit`
#       fields: [text, summary, attachment_reference]
#       starterPatterns: [sk_prefix, bearer_header, api_key_header]
#   files:
#     pii:
#       fields: [name, content, extracted_text, transcript]
#       uninspectable: allow

endpoints:
  # agents:
  #   maxSubagents: 20 # Default: 10; valid range: 1-50
  #   eventDriven:
  #     selfUrl: 'https://librechat.internal' # Usually omitted; only for trusted internal HTTP admission
  #   # Stateful Code Sessions are highly experimental and opt-in through capabilities.
  #   statefulCodeSessions:
  #     allowedEnvironments: ['user', 'agent-user', 'conversation']
  #     environments:
  #       - id: managed-default
  #         name: Managed Code API
  #         type: managed
  #         baseURL: https://code.example.com/v1
  #         default: true
  #       - id: engineering-vm
  #         name: Engineering VM
  #         type: attached
  #         baseURL: https://code-bridge.example.com/v1
  #         # Pins an administrator-managed environment to one worker.
  #         pairing:
  #           workerId: engineering-vm
  #           tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
  #       - id: personal-workers
  #         name: Personal Code Workers
  #         type: attached
  #         baseURL: https://code-bridge.example.com/v1
  #         configSchema:
  #           permissions:
  #             fileWrite:
  #               allowed: [allow, ask, deny]
  #               default: ask
  #             commandExecution:
  #               allowed: [ask, deny]
  #               default: ask
  #         # Lets authorized users pair owner-bound outbound workers.
  #         pairing:
  #           allowPrincipalWorkers: true
  #           tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
  #   # Automatic completion delivery is on by default. Set false for poll-only background tasks.
  #   backgroundTasks:
  #     completionWakeups: true

  custom:
    # Anyscale
    - name: 'Anyscale'
      apiKey: '${ANYSCALE_API_KEY}'
      baseURL: 'https://api.endpoints.anyscale.com/v1'
      models:
        default: ['meta-llama/Llama-2-7b-chat-hf']
        fetch: true
      titleConvo: true
      titleModel: 'meta-llama/Llama-2-7b-chat-hf'
      summarize: false
      summaryModel: 'meta-llama/Llama-2-7b-chat-hf'
      modelDisplayLabel: 'Anyscale'

    # APIpie
    - name: 'APIpie'
      apiKey: '${APIPIE_API_KEY}'
      baseURL: 'https://apipie.ai/v1/'
      models:
        default:
          [
            'gpt-4',
            'gpt-4-turbo',
            'gpt-3.5-turbo',
            'claude-3-opus',
            'claude-3-sonnet',
            'claude-3-haiku',
            'llama-3-70b-instruct',
            'llama-3-8b-instruct',
            'gemini-pro-1.5',
            'gemini-pro',
            'mistral-large',
            'mistral-medium',
            'mistral-small',
            'mistral-tiny',
            'mixtral-8x22b',
          ]
        fetch: false
      titleConvo: true
      titleModel: 'gpt-3.5-turbo'
      dropParams: ['stream']

    #cohere
    - name: 'cohere'
      apiKey: '${COHERE_API_KEY}'
      baseURL: 'https://api.cohere.ai/v1'
      models:
        default:
          [
            'command-r',
            'command-r-plus',
            'command-light',
            'command-light-nightly',
            'command',
            'command-nightly',
          ]
        fetch: false
      modelDisplayLabel: 'cohere'
      titleModel: 'command'
      dropParams: ['stop', 'user', 'frequency_penalty', 'presence_penalty', 'temperature', 'top_p']

    # Fireworks
    - name: 'Fireworks'
      apiKey: '${FIREWORKS_API_KEY}'
      baseURL: 'https://api.fireworks.ai/inference/v1'
      models:
        default: ['accounts/fireworks/models/mixtral-8x7b-instruct']
        fetch: true
      titleConvo: true
      titleModel: 'accounts/fireworks/models/llama-v2-7b-chat'
      summarize: false
      summaryModel: 'accounts/fireworks/models/llama-v2-7b-chat'
      modelDisplayLabel: 'Fireworks'
      dropParams: ['user']

    # groq
    - name: 'groq'
      apiKey: '${GROQ_API_KEY}'
      baseURL: 'https://api.groq.com/openai/v1/'
      models:
        default:
          [
            'llama2-70b-4096',
            'llama3-70b-8192',
            'llama3-8b-8192',
            'mixtral-8x7b-32768',
            'gemma-7b-it',
          ]
        fetch: false
      titleConvo: true
      titleModel: 'mixtral-8x7b-32768'
      modelDisplayLabel: 'groq'

    # Mistral AI API
    - name: 'Mistral'
      apiKey: '${MISTRAL_API_KEY}'
      baseURL: 'https://api.mistral.ai/v1'
      models:
        default: ['mistral-tiny', 'mistral-small', 'mistral-medium', 'mistral-large-latest']
        fetch: true
      titleConvo: true
      titleModel: 'mistral-tiny'
      modelDisplayLabel: 'Mistral'
      dropParams: ['stop', 'user', 'frequency_penalty', 'presence_penalty']

    # OpenRouter.ai
    - name: 'OpenRouter'
      apiKey: '${OPENROUTER_KEY}'
      baseURL: 'https://openrouter.ai/api/v1'
      models:
        default: ['openai/gpt-3.5-turbo']
        fetch: true
      titleConvo: true
      titleModel: 'gpt-3.5-turbo'
      summarize: false
      summaryModel: 'gpt-3.5-turbo'
      modelDisplayLabel: 'OpenRouter'

    # Perplexity
    - name: 'Perplexity'
      apiKey: '${PERPLEXITY_API_KEY}'
      baseURL: 'https://api.perplexity.ai/'
      models:
        default:
          [
            'mistral-7b-instruct',
            'sonar-small-chat',
            'sonar-small-online',
            'sonar-medium-chat',
            'sonar-medium-online',
          ]
        fetch: false # fetching list of models is not supported
      titleConvo: true
      titleModel: 'sonar-medium-chat'
      summarize: false
      summaryModel: 'sonar-medium-chat'
      dropParams: ['stop', 'frequency_penalty']
      modelDisplayLabel: 'Perplexity'

    # ShuttleAI API
    - name: 'ShuttleAI'
      apiKey: '${SHUTTLEAI_API_KEY}'
      baseURL: 'https://api.shuttleai.app/v1'
      models:
        default: ['shuttle-1', 'shuttle-turbo']
        fetch: true
      titleConvo: true
      titleModel: 'gemini-pro'
      summarize: false
      summaryModel: 'llama-summarize'
      modelDisplayLabel: 'ShuttleAI'
      dropParams: ['user']

    # together.ai
    - name: 'together.ai'
      apiKey: '${TOGETHERAI_API_KEY}'
      baseURL: 'https://api.together.xyz'
      models:
        default:
          [
            'zero-one-ai/Yi-34B-Chat',
            'Austism/chronos-hermes-13b',
            'DiscoResearch/DiscoLM-mixtral-8x7b-v2',
            'Gryphe/MythoMax-L2-13b',
            'lmsys/vicuna-13b-v1.5',
            'lmsys/vicuna-7b-v1.5',
            'lmsys/vicuna-13b-v1.5-16k',
            'codellama/CodeLlama-13b-Instruct-hf',
            'codellama/CodeLlama-34b-Instruct-hf',
            'codellama/CodeLlama-70b-Instruct-hf',
            'codellama/CodeLlama-7b-Instruct-hf',
            'togethercomputer/llama-2-13b-chat',
            'togethercomputer/llama-2-70b-chat',
            'togethercomputer/llama-2-7b-chat',
            'NousResearch/Nous-Capybara-7B-V1p9',
            'NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO',
            'NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT',
            'NousResearch/Nous-Hermes-Llama2-70b',
            'NousResearch/Nous-Hermes-llama-2-7b',
            'NousResearch/Nous-Hermes-Llama2-13b',
            'NousResearch/Nous-Hermes-2-Yi-34B',
            'openchat/openchat-3.5-1210',
            'Open-Orca/Mistral-7B-OpenOrca',
            'togethercomputer/Qwen-7B-Chat',
            'snorkelai/Snorkel-Mistral-PairRM-DPO',
            'togethercomputer/alpaca-7b',
            'togethercomputer/falcon-40b-instruct',
            'togethercomputer/falcon-7b-instruct',
            'togethercomputer/GPT-NeoXT-Chat-Base-20B',
            'togethercomputer/Llama-2-7B-32K-Instruct',
            'togethercomputer/Pythia-Chat-Base-7B-v0.16',
            'togethercomputer/RedPajama-INCITE-Chat-3B-v1',
            'togethercomputer/RedPajama-INCITE-7B-Chat',
            'togethercomputer/StripedHyena-Nous-7B',
            'Undi95/ReMM-SLERP-L2-13B',
            'Undi95/Toppy-M-7B',
            'WizardLM/WizardLM-13B-V1.2',
            'garage-bAInd/Platypus2-70B-instruct',
            'mistralai/Mistral-7B-Instruct-v0.1',
            'mistralai/Mistral-7B-Instruct-v0.2',
            'mistralai/Mixtral-8x7B-Instruct-v0.1',
            'teknium/OpenHermes-2-Mistral-7B',
            'teknium/OpenHermes-2p5-Mistral-7B',
            'upstage/SOLAR-10.7B-Instruct-v1.0',
          ]
        fetch: false # fetching list of models is not supported
      titleConvo: true
      titleModel: 'togethercomputer/llama-2-7b-chat'
      summarize: false
      summaryModel: 'togethercomputer/llama-2-7b-chat'
      modelDisplayLabel: 'together.ai'
```

</Callout>

## Example with Comments

This example configuration file sets up LibreChat with detailed options across several key areas:

- **Caching**: Enabled to improve performance.
- **File Handling**:
  - **File Strategy**: Commented out but hints at possible integration with Firebase for file storage.
  - **File Configurations**: Customizes file upload limits and allowed MIME types for different endpoints, including a global server file size limit and a specific limit for user avatar images.
- **Rate Limiting**: Defines thresholds for the maximum number of file uploads allowed per IP and user within a specified time window, aiming to prevent abuse.
- **Registration**:
  - Allows registration from specified social login providers and email domains, enhancing security and user management.
- **Endpoints**:
  - **Assistants**: Configures the assistants' endpoint with a polling interval and a timeout for operations, and provides an option to disable the builder interface.
  - **Custom Endpoints**:
    - Configures two external AI service endpoints, Mistral and OpenRouter, including API keys, base URLs, model handling, and specific feature toggles like conversation titles, summarization, and parameter adjustments.
    - For Mistral, it enables dynamic model fetching, applies additional parameters for safe prompts, and explicitly drops unsupported parameters.
    - For OpenRouter, it sets up a basic configuration without dynamic model fetching and specifies a model for conversation titles.

<Callout type="example" title="Commented Example" collapsible>

```yaml filename="librechat.yaml"
# For more information, see the Configuration Guide:
# https://www.librechat.ai/docs/configuration/librechat_yaml

# Configuration version (required)
version: 1.3.15

# Cache settings: Set to true to enable caching
cache: true

# Custom interface configuration
interface:
  # MCP Servers UI configuration
  mcpServers:
    placeholder: 'MCP Servers'

  # Experimental Scheduled Chats are disabled when omitted.
  # schedules:
  #   use: true
  #   create: true
  #   maxPerUser: 10
  #   minIntervalMinutes: 60
  #   autoDisableAfterFailures: 5
  #   fireConcurrency: 5
  #   requireProject: false
  #   # projectId: 'project-id' # Pins every schedule to one project and implies requireProject

  # Show the thumbs up/thumbs down feedback buttons on responses (default: true)
  # feedback: true

  # Privacy policy settings
  privacyPolicy:
    externalUrl: 'https://librechat.ai/privacy-policy'
    openNewTab: true

  # Terms of service
  termsOfService:
    externalUrl: 'https://librechat.ai/tos'
    openNewTab: true

# Example Registration Object Structure (optional)
registration:
  socialLogins: ['github', 'google', 'discord', 'openid', 'facebook']
  # allowedDomains:
  # - "gmail.com"

# rateLimits:
#   agentEvents:
#     userMax: 40
#     userWindowInMinutes: 1
#   fileUploads:
#     ipMax: 100
#     ipWindowInMinutes: 60  # Rate limit window for file uploads per IP
#     userMax: 50
#     userWindowInMinutes: 60  # Rate limit window for file uploads per user
#   conversationsImport:
#     ipMax: 100
#     ipWindowInMinutes: 60  # Rate limit window for conversation imports per IP
#     userMax: 50
#     userWindowInMinutes: 60  # Rate limit window for conversation imports per user

# Source-aware content filters are disabled when omitted. `filters` is loaded
# only from the base config; role, group, and user overrides cannot change it.
# filters:
#   messages:
#     unattributedAssistantContent: model_output
#     pii:
#       action: audit # `block` (default) or `audit`
#       fields: [text, summary, attachment_reference]
#       starterPatterns: [sk_prefix, bearer_header, api_key_header]
#   files:
#     pii:
#       fields: [name, content, extracted_text, transcript]
#       uninspectable: allow
#   skills:
#     pii:
#       fields: [name, description, instructions, imported_text, file_text]

# Definition of custom endpoints
endpoints:
  # all:
  #   headers:
  #     X-App: 'librechat'
  # openAI:
  #   headers:
  #     X-Gateway-Metadata: '{"user_email":"{{LIBRECHAT_USER_EMAIL}}"}'
  # anthropic:
  #   headers:
  #     X-Conversation-Id: '{{LIBRECHAT_BODY_CONVERSATIONID}}'
  # google:
  #   headers:
  #     X-Gateway-Metadata: '{"user_id":"{{LIBRECHAT_USER_ID}}"}'

  # assistants:
  #   disableBuilder: false # Disable Assistants Builder Interface by setting to `true`
  #   pollIntervalMs: 750  # Polling interval for checking assistant updates
  #   timeoutMs: 180000  # Timeout for assistant operations
  #   # Should only be one or the other, either `supportedIds` or `excludedIds`
  #   supportedIds: ["asst_supportedAssistantId1", "asst_supportedAssistantId2"]
  #   # excludedIds: ["asst_excludedAssistantId"]
  #   Only show assistants that the user created or that were created externally (e.g. in Assistants playground).
  #   # privateAssistants: false # Does not work with `supportedIds` or `excludedIds`
  #   # (optional) Models that support retrieval, will default to latest known OpenAI models that support the feature
  #   retrievalModels: ["gpt-4-turbo-preview"]
  #   # (optional) Assistant Capabilities available to all users. Omit the ones you wish to exclude. Defaults to list below.
  #   capabilities: ["code_interpreter", "retrieval", "actions", "tools", "image_vision"]
  # agents:
  #   titleTiming: immediate # "immediate" (default) or "final"
  #   maxSubagents: 20 # Maximum explicit subagents per agent; default 10, hard cap 50
  #   maxToolCallArgBytes: 65536 # 64 KiB per streamed tool call; 0 disables the global guard
  #   maxDeltaEventsPerTurn: 100000 # Optional event cap; omitted or 0 disables it
  #   maxToolCallArgBytesByTool:
  #     create_file: 131072 # Overrides the global limit for this tool; 0 disables its guard
  #   eventDriven:
  #     selfUrl: 'https://librechat.internal' # Usually omitted; only for trusted internal HTTP admission
  #   # Automatic completion delivery is on by default. Set false for poll-only background tasks.
  #   backgroundTasks:
  #     completionWakeups: true
  #   skills:
  #     maxCatalogSkills: 20
  custom:
    # Anthropic-compatible Example (native /v1/messages API)
    # - name: 'Claude-Compatible'
    #   provider: 'anthropic'
    #   apiKey: '${ANTHROPIC_API_KEY}'
    #   baseURL: 'https://api.anthropic.com'
    #   headers:
    #     anthropic-version: '2023-06-01'
    #   models:
    #     default:
    #       - 'claude-sonnet-4-5'
    #       - 'claude-opus-4-5'
    #     fetch: false
    #   titleConvo: true
    #   titleModel: 'claude-sonnet-4-5'
    #   modelDisplayLabel: 'Claude (Compatible)'

    # Groq Example
    - name: 'groq'
      apiKey: '${GROQ_API_KEY}'
      baseURL: 'https://api.groq.com/openai/v1/'
      models:
        default:
          [
            'llama3-70b-8192',
            'llama3-8b-8192',
            'llama2-70b-4096',
            'mixtral-8x7b-32768',
            'gemma-7b-it',
          ]
        fetch: false
      titleConvo: true
      titleModel: 'mixtral-8x7b-32768'
      modelDisplayLabel: 'groq'

    # Mistral AI Example
    - name: 'Mistral' # Unique name for the endpoint
      # For `apiKey` and `baseURL`, you can use environment variables that you define.
      # recommended environment variables:
      apiKey: '${MISTRAL_API_KEY}'
      baseURL: 'https://api.mistral.ai/v1'

      # Models configuration
      models:
        # List of default models to use. At least one value is required.
        default: ['mistral-tiny', 'mistral-small', 'mistral-medium']
        # Fetch option: Set to true to fetch models from API.
        fetch: true # Defaults to false.

      # Optional configurations

      # Title Conversation setting
      titleConvo: true # Set to true to enable title conversation
      # titleTiming: "immediate" # Generate titles immediately, or use "final" for legacy behavior

      # Title Method: Choose between "completion" or "functions".
      # titleMethod: "completion"  # Defaults to "completion" if omitted.

      # Title Model: Specify the model to use for titles.
      titleModel: 'mistral-tiny' # Defaults to "gpt-3.5-turbo" if omitted.

      # Summarize setting: Set to true to enable summarization.
      # summarize: false

      # Summary Model: Specify the model to use if summarization is enabled.
      # summaryModel: "mistral-tiny"  # Defaults to "gpt-3.5-turbo" if omitted.

      # The label displayed for the AI model in messages.
      modelDisplayLabel: 'Mistral' # Default is "AI" when not set.

      # Add additional parameters to the request. Default params will be overwritten.
      # addParams:
      # safe_prompt: true # This field is specific to Mistral AI: https://docs.mistral.ai/api/

      # Custom endpoint behavior flags, not sent directly as provider params.
      # customParams:
      #   reasoningFormat: reasoning_object # reasoning_effort, reasoning_object, or disabled
      #   reasoningKey: reasoning_content # reasoning or reasoning_content

      # Optional per-model context window and pricing for usage/cost tracking
      # tokenConfig:
      #   mistral-large-latest:
      #     prompt: 2
      #     completion: 6
      #     context: 128000

      # Drop Default params parameters from the request. See default params in guide linked below.
      # NOTE: For Mistral, it is necessary to drop the following parameters or you will encounter a 422 Error:
      dropParams: ['stop', 'user', 'frequency_penalty', 'presence_penalty']

    # OpenRouter Example
    - name: 'OpenRouter'
      # For `apiKey` and `baseURL`, you can use environment variables that you define.
      # recommended environment variables:
      # Known issue: you should not use `OPENROUTER_API_KEY` as it will then override the `openAI` endpoint to use OpenRouter as well.
      apiKey: '${OPENROUTER_KEY}'
      baseURL: 'https://openrouter.ai/api/v1'
      models:
        default: ['meta-llama/llama-3-70b-instruct']
        fetch: true
      titleConvo: true
      titleModel: 'meta-llama/llama-3-70b-instruct'
      # Recommended: Drop the stop parameter from the request as Openrouter models use a variety of stop tokens.
      dropParams: ['stop']
      modelDisplayLabel: 'OpenRouter'

# modelSpecs:
#   list:
#     - name: "default-assistant"
#       label: "Default Assistant"
#       softDefault: true # First-time default only; does not override later user selections
#       showOnLanding: true # Show this spec label/description on the chat landing
#       conversation_starters:
#         - "Draft a project plan"
#         - "Summarize this document"
#       skills:
#         - "brand-guidelines"
#       subagents:
#         enabled: true
#         allowSelf: true
#         agent_ids: []
#       preset:
#         endpoint: "agents"
#         model: "gpt-4o"

# messageFilter:
#   pii:
#     starterPatterns: [sk_prefix, bearer_header, api_key_header]
#     # Custom patterns use RE2 syntax. Backreferences and lookaround are unsupported.
#     # Invalid patterns are rejected when LibreChat loads the configuration.
#     customPatterns:
#       - id: anthropic_api_key
#         label: Anthropic API key
#         regex: "sk-ant-[A-Za-z0-9_-]{20,}"

# Speech, OCR, and web-search connections block private destinations at connect time.
# Use exact private host:port exemptions only for trusted self-hosted services.
# speech:
#   tts:
#     allowedAddresses: ['tts.internal:8080']
#   stt:
#     allowedAddresses: ['stt.internal:8080']
# ocr:
#   allowedAddresses: ['ocr.internal:8080']
# webSearch:
#   allowedAddresses: ['searxng:8080']
#
# A fully keyless Keenable stack (optional keys only raise public rate limits):
# webSearch:
#   searchProvider: keenable
#   scraperProvider: keenable
#   rerankerType: none
#   # keenableApiKey: '${KEENABLE_API_KEY}'
#   # keenableApiUrl: '${KEENABLE_API_URL}' # Search only; fetch uses KEENABLE_FETCH_URL
#   keenableSearchOptions:
#     maxResults: 8
#     # site: example.com
#     # attributionTitle: LibreChat
#     # timeout: 15000
#   keenableScraperOptions:
#     # attributionTitle: LibreChat
#     timeout: 15000

# fileConfig:
#   endpoints:
#     assistants:
#       fileLimit: 5
#       fileSizeLimit: 10  # Maximum size for an individual file in MB
#       totalSizeLimit: 50  # Maximum total size for all files in a single request in MB
#       supportedMimeTypes:
#         # RE2 syntax: backreferences and lookaround are unsupported.
#         - "image/.*"
#         - "application/pdf"
#     openAI:
#       disabled: true  # Disables file uploading to the OpenAI endpoint
#     default:
#       totalSizeLimit: 20
#     YourCustomEndpointName:
#       fileLimit: 2
#       fileSizeLimit: 5
#   serverFileSizeLimit: 100  # Global server file size limit in MB
#   avatarSizeLimit: 2  # Limit for user avatar image size in MB
# See the Custom Configuration Guide for more information:
# https://www.librechat.ai/docs/configuration/librechat_yaml
```

</Callout>


# AI Endpoints (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints)

## Intro

- This section lists known, compatible AI Endpoints, also known as "Custom Endpoints," with example setups for the `librechat.yaml` file, also known as the [Custom Config](/docs/configuration/librechat_yaml) file.

- In all of the examples, arbitrary environment variable names are defined but you can use any name you wish, as well as changing the value to `user_provided` to allow users to submit their own API key from the web UI.

<Callout type="warning" title="Important: 'user_provided' Key Setting">
When setting API keys to "user_provided", this allows users to enter their own API keys through the web interface. This is different from the pre-configured endpoints in the .env file where you would set `ENDPOINT_KEY=user_provided` (e.g., `OPENAI_API_KEY=user_provided`).

For custom endpoints in librechat.yaml, you would use:
```yaml
endpoints:
  custom:
    - name: "Your Endpoint"
      apiKey: "user_provided"  # No need for ${} syntax here
```

For environment variables in the .env file, you would use:
```bash
OPENAI_API_KEY=user_provided
```
</Callout>

- Some of the endpoints are marked as **Known,** which means they might have special handling and/or an icon already provided in the app for you.

### Notes

- It's recommended you follow the [Custom Endpoints Quick Start Guide](/docs/quick_start/custom_endpoints) before proceeding with the examples below.
- Important: make sure you setup the `librechat.yaml` file correctly: **[setup documentation](/docs/configuration/librechat_yaml)**.


# Anyscale (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/anyscale)

Anyscale Endpoints serves open models such as Llama through an OpenAI-compatible API that you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key from your [Anyscale credentials](https://app.endpoints.anyscale.com/credentials). Add it to your `.env` file:

```bash filename=".env"
ANYSCALE_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Anyscale"
      apiKey: "${ANYSCALE_API_KEY}"
      baseURL: "https://api.endpoints.anyscale.com/v1"
      models:
        default: [
          "meta-llama/Llama-2-7b-chat-hf",
          ]
        fetch: true
      titleConvo: true
      titleModel: "meta-llama/Llama-2-7b-chat-hf"
      summarize: false
      summaryModel: "meta-llama/Llama-2-7b-chat-hf"
      modelDisplayLabel: "Anyscale"
```

## Notes

- With `fetch: true`, LibreChat loads the available model list from Anyscale, so the `default` array is only the initial selection.


# APIpie (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/apipie)

APIpie is an aggregator that exposes models from many providers through a single OpenAI-compatible endpoint.

## Get an API key

Create a key from [apipie.ai/dashboard/profile/api-keys](https://apipie.ai/dashboard/profile/api-keys) and add it to your `.env` file:

```bash filename=".env"
APIPIE_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "APIpie"
      apiKey: "${APIPIE_API_KEY}"
      baseURL: "https://apipie.ai/v1/"
      models:
        default: [
          "gpt-4",
          "gpt-4-turbo",
          "gpt-3.5-turbo",
          "claude-3-opus",
          "claude-3-sonnet",
          "claude-3-haiku",
          "llama-3-70b-instruct",
          "llama-3-8b-instruct",
          "gemini-pro-1.5",
          "gemini-pro",
          "mistral-large",
          "mistral-medium",
          "mistral-small",
          "mistral-tiny",
          "mixtral-8x22b",
          ]
        fetch: false
      titleConvo: true
      titleModel: "claude-3-haiku"
      summarize: false
      summaryModel: "claude-3-haiku"
      dropParams: ["stream"]
      modelDisplayLabel: "APIpie"
```

<Callout type="tip" title="Fetch and order the models" collapsible>
This python script can fetch and order the LLM models for you. The output is saved to `models.txt`, formatted so it is easier to drop into the yaml config.

```py filename="fetch.py"
import json
import requests

def fetch_and_order_models():
    # API endpoint
    url = "https://apipie.ai/models"

    # headers as per request example
    headers = {"Accept": "application/json"}

    # request parameters
    params = {"type": "llm"}

    # make request
    response = requests.get(url, headers=headers, params=params)

    # parse JSON response
    data = response.json()

    # extract an ordered list of unique model IDs
    model_ids = sorted(set([model["id"] for model in data]))

    # write result to a text file
    with open("models.txt", "w") as file:
        json.dump(model_ids, file, indent=2)

# execute the function
if __name__ == "__main__":
    fetch_and_order_models()
```
</Callout>

## Notes

- Automatic model fetching is not supported, so `fetch` is set to `false` and the model list is defined inline. Use the script above to regenerate the list when new models are added.
- Conversation titling results can be inconsistent.
- `dropParams: ["stream"]` removes the `stream` parameter, which is not currently supported.


# Azure OpenAI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/azure)

# Azure OpenAI Integration for LibreChat

LibreChat supports Azure OpenAI API services as a first-class endpoint. To use Azure OpenAI within LibreChat, configure the [`librechat.yaml` file](/docs/configuration/librechat_yaml/object_structure/azure_openai) for your setup. This document covers the setup process for using multiple deployments and models.

## Example

An example configuration including many of the options and features described below:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    # Endpoint-level configuration
    titleModel: "llama-70b-chat"
    plugins: true
    assistants: true
    groups:
    # Group-level configuration
    - group: "my-resource-westus"
      apiKey: "${WESTUS_API_KEY}"
      instanceName: "my-resource-westus"
      version: "2024-03-01-preview"
      # Model-level configuration
      models:
        gpt-4-vision-preview:
          deploymentName: gpt-4-vision-preview
          version: "2024-03-01-preview"
        gpt-3.5-turbo:
          deploymentName: gpt-35-turbo
        gpt-4-1106-preview:
          deploymentName: gpt-4-1106-preview
    # Group-level configuration
    - group: "mistral-inference"
      apiKey: "${AZURE_MISTRAL_API_KEY}"
      baseURL: "https://Mistral-large-vnpet-serverless.region.inference.ai.azure.com/v1/chat/completions"
      serverless: true
      # Model-level configuration
      models:
        mistral-large: true
    # Group-level configuration
    - group: "my-resource-sweden"
      apiKey: "${SWEDEN_API_KEY}"
      instanceName: "my-resource-sweden"
      deploymentName: gpt-4-1106-preview
      version: "2024-03-01-preview"
      assistants: true
      # Model-level configuration
      models:
        gpt-4-turbo: true
```

This example follows the [Azure OpenAI Endpoint Configuration Docs](/docs/configuration/librechat_yaml/object_structure/azure_openai).

Each level of configuration is detailed in its respective section:

1. [Endpoint-level config](#endpoint-level-configuration)

2. [Group-level config](#group-level-configuration)

3. [Model-level config](#model-level-configuration)

## Setup

1. **Open `librechat.yaml` for Editing**: Use your preferred text editor or IDE to open and edit the `librechat.yaml` file.

    - Optional: use a remote or custom file path with the following environment variable:

    ```sh filename=".env"
    CONFIG_PATH="/alternative/path/to/librechat.yaml"
    ```

2. **Configure Azure OpenAI Settings**: Follow the structure outlined below to populate your Azure OpenAI settings, including API keys, instance names, model groups, and other configurations.

3. **Remove Legacy Settings**: If you are using any of the legacy configurations, remove them. The LibreChat server will also detect these and remind you.

4. **Save Your Changes**: Save the `librechat.yaml` file.

5. **Restart LibreChat**: Restart your LibreChat application so the updated configuration is loaded.

## Required Fields

To integrate Azure OpenAI with LibreChat, specific fields must be configured in your `librechat.yaml` file. These fields are validated through a combination of custom and environment variables. The detailed requirements follow.

## Endpoint-Level Configuration

#### Global Azure Settings:

**Title and Conversation Settings:**
<OptionTable
  options={[
    ['titleModel', 'string', 'Specifies the model to use for generating conversation titles. If not provided, the default model is set as `gpt-3.5-turbo`, which will result in no titles if lacking this model. You can also set this to dynamically use the current model by setting it to `current_model`.', 'titleModel:'],
    ['plugins', 'boolean', 'Enables the use of plugins through Azure. Set to `true` to activate Plugins endpoint support through your Azure config. Default: `false`.', 'plugins:false'],
    ['assistants', 'boolean', 'Enables the use of assistants through Azure. Set to `true` to activate Assistants endpoint through your Azure config. Default: `false`. Note: this requires an assistants-compatible region.', 'assistants:false'],
    ['summarize', 'boolean', 'Enables conversation summarization for all Azure models. Set to `true` to activate summarization. Default: `false`.', 'summarize:false'],
    ['summaryModel', 'string', 'Specifies the model to use for generating conversation summaries. If not provided, the default behavior is to use the first model in the `default` array of the first group.', 'summaryModel:'],
    ['titleConvo', 'boolean', 'Enables conversation title generation for all Azure models. Set to `true` to activate title generation. Default: `false`.', 'titleConvo:false'],
    ['titleMethod', 'string', 'Controls the method used for generating conversation titles. Valid values: "completion" (default), "structured", "functions" (legacy alias for "structured").', 'titleMethod:completion'],
    ['titlePrompt', 'string', 'Custom prompt for title generation. Must include {convo} placeholder for the conversation content.', 'See documentation for default prompt'],
    ['titlePromptTemplate', 'string', 'Template for formatting conversation content. Must include {input} and {output} placeholders. Default: "User: {input}\\nAI: {output}"', 'titlePromptTemplate:'],
    ['titleEndpoint', 'string', 'Alternative endpoint to use for title generation. Accepted values: openAI, azureOpenAI, google, anthropic, bedrock, or custom endpoint names.', 'titleEndpoint:'],
  ]}
/>

**Group Configuration:**
<OptionTable
  options={[
    ['groups', 'array', 'Specifies the list of Azure OpenAI model groups. Each group represents a set of models with shared configurations. The groups field is an array of objects, where each object defines the settings for a specific group. This is a required field at the endpoint level, and at least one group must be defined. The group-level configurations are detailed in the Group-Level Configuration section.', '# groups:[]'],
  ]}
/>

**Custom Order (Optional):**
<OptionTable
  options={[
    ['customOrder', 'number', 'Allows you to specify a custom order for the Azure endpoint in the user interface. Higher numbers will appear lower in the list. If not provided, the default order is determined by the order in which the endpoints are defined in the `librechat.yaml` file.', 'customOrder:'],
  ]}
/>

The `customOrder` option is commented out, as it is optional.

Example of these endpoint-level settings in your `librechat.yaml` file:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    titleModel: "gpt-3.5-turbo-1106"
    plugins: true
    assistants: true
    summarize: true
    summaryModel: "gpt-3.5-turbo-1106"
    titleConvo: true
    titleMethod: "functions"
    groups:
      # ... (group-level and model-level configurations)
```

## Group-Level Configuration

The fields configurable in the Custom Config (`librechat.yaml`) file. For more information on each field, see the [Azure OpenAI section in the Custom Config Docs](/docs/configuration/librechat_yaml/object_structure/azure_openai).

Group-Level Configuration:
Group Identification:
<OptionTable
options={[
['group', 'string', 'Unique identifier name for a group of models. Duplicate group names are not allowed and will result in validation errors.', 'group: default'],
]}
/>

Authentication:
<OptionTable
options={[
['apiKey', 'string', 'Must be a valid API key for Azure OpenAI services. It could be a direct key string or an environment variable reference (e.g., ${WESTUS_API_KEY}).', 'apiKey: ${AZURE_API_KEY}'],
]}
/>

Azure OpenAI Instance:
<OptionTable
options={[
['instanceName', 'string', 'Name of the Azure OpenAI instance. This field can also support environment variable references. **Supports both domain formats**: `.openai.azure.com` (legacy) and `.cognitiveservices.azure.com` (new). You can specify either the full domain (e.g., `my-instance.cognitiveservices.azure.com`) or just the instance name (e.g., `my-instance`) for backward compatibility with the legacy `.openai.azure.com` format.', 'instanceName: ${AZURE_OPENAI_INSTANCE}'],
]}
/>

Deployment Configuration:
<OptionTable
options={[
['deploymentName', 'string', 'The deployment name at the group level is optional but required if any model within the group is set to true.', 'deploymentName: my-deployment'],
['version', 'string', 'The Azure OpenAI API version at the group level is optional but required if any model within the group is set to true.', 'version: 2023-03-15-preview'],
]}
/>

Advanced Settings:
<OptionTable
options={[
['baseURL', 'string', 'Custom base URL for the Azure OpenAI API requests. Environment variable references are supported. This is optional and can be used for advanced routing scenarios.', 'baseURL: https://my-custom-base-url.com'],
['additionalHeaders', 'object', 'Specifies any extra headers for Azure OpenAI API requests as key-value pairs. Environment variable references can be included as values.', 'additionalHeaders: {Authorization: ${AUTH_HEADER}}'],
['serverless', 'boolean', 'Specifies if the group is a serverless inference chat completions endpoint from Azure Model Catalog, for which only a model identifier, baseURL, and apiKey are needed. For more info, see serverless inference endpoints.', 'serverless: true'],
['addParams', 'object', 'Adds or overrides additional parameters for Azure OpenAI API requests. Useful for specifying API-specific options as key-value pairs.', 'addParams: {temperature: 0.7}'],
['dropParams', 'array', 'Allows for the exclusion of certain default parameters from Azure OpenAI API requests. Useful for APIs that do not accept or recognize specific parameters. This should be specified as a list of strings.', 'dropParams: [top_p, stop]'],
]}
/>

Model Configuration:
<OptionTable
options={[
['models', 'object', 'Specifies the mapping of model identifiers to their configurations within the group. The keys represent the model identifiers, which must match the corresponding OpenAI model names. The values can be either boolean (true) or objects containing model-specific settings. If a model is set to true, it inherits the group-level deploymentName and version. If a model is configured as an object, it can have its own deploymentName and version. This field is required, and at least one model must be defined within each group. More info here', 'models: {gpt-3.5-turbo: true, text-davinci-003: {}}'],
]}
/>

Example of a group-level configuration in the `librechat.yaml` file:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    # ... (endpoint-level configurations)
    groups:
      - group: "my-resource-group"
        apiKey: "${AZURE_API_KEY}"
        instanceName: "my-instance"
        deploymentName: "gpt-35-turbo"
        version: "2023-03-15-preview"
        baseURL: "https://my-instance.openai.azure.com/"
        additionalHeaders:
          CustomHeader: "HeaderValue"
        addParams:
          max_tokens: 2048
          temperature: 0.7
        dropParams:
          - "frequency_penalty"
          - "presence_penalty"
        models:
        # ... (model-level configurations)
```

## Model-Level Configuration

Within each group, the `models` field contains a mapping of model identifiers to their configurations:

Model Identification:
<OptionTable
options={[
['Model Identifier', 'string', 'Must match the corresponding OpenAI model name. Can be a partial match.', 'gpt-3.5-turbo: true'],
]}
/>

Model Configuration:
<OptionTable
options={[
['Model Configuration', 'boolean/object', 'Boolean true: Uses the group-level deploymentName and version. Object: Specifies model-specific deploymentName and version. If not provided, inherits from the group.', 'text-davinci-003: {deploymentName: my-model-deployment, version: 2023-03-15-preview}'],
['deploymentName', 'string', 'The deployment name for this specific model.', 'deploymentName: my-model-deployment'],
['version', 'string', 'The Azure OpenAI API version for this specific model.', 'version: 2023-03-15-preview'],
]}
/>

Serverless Inference Endpoints:
<OptionTable
options={[
['Serverless Inference Endpoints', 'note', 'For serverless models, set the model to true.', 'gpt-4: true'],
]}
/>

- The **model identifier must match its corresponding OpenAI model name** in order for it to properly reflect its known context limits and/or function in the case of vision. For example, if you intend to use gpt-4-vision, it must be configured like so:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    # ... (endpoint-level configurations)
    groups:
    # ... (group-level configurations)
    - group: "example_group"
    models:
     # Model identifiers must match OpenAI Model name (can be a partial match)
      gpt-4-vision-preview:
      # Object setting: must include at least "deploymentName" and/or "version"
        deploymentName: "arbitrary-deployment-name"
        version: "2024-02-15-preview" # version can be any that supports vision
      # Boolean setting, must be "true"
      gpt-4-turbo: true
```

- See [Model Deployments](#model-deployments) for more examples.

- If a model is set to `true`, it implies using the group-level `deploymentName` and `version` for this model. Both must be defined at the group level in this case.
  
- If a model is configured as an object, it can specify its own `deploymentName` and `version`. If these are not provided, the model inherits the group's `deploymentName` and `version`.

- If the group represents a [serverless inference endpoint](#serverless-inference-endpoints), the singular model should be set to `true` to add it to the models list.

### Special Considerations

1. **Unique Names**: Both model and group names must be unique across the entire configuration. Duplicate names lead to validation failures.

2. **Missing Required Fields**: Lack of required `deploymentName` or `version` either at the group level (for boolean-flagged models) or within the models' configurations (if not inheriting or explicitly specified) will result in validation errors, unless the group represents a [serverless inference endpoint](#serverless-inference-endpoints).

3. **Environment Variable References**: The configuration supports environment variable references (e.g., `${VARIABLE_NAME}`). Ensure that all referenced variables are present in your environment to avoid runtime errors. The absence of defined environment variables referenced in the config will cause errors.`${INSTANCE_NAME}` and `${DEPLOYMENT_NAME}` are unique placeholders, and do not correspond to environment variables, but instead correspond to the instance and deployment name of the currently selected model. It is not recommended you use `INSTANCE_NAME` and `DEPLOYMENT_NAME` as environment variable names to avoid any potential conflicts.

4. **Error Handling**: Any issues in the config, like duplicate names, undefined environment variables, or missing required fields, will invalidate the setup and generate descriptive error messages aiming for prompt resolution. You will not be allowed to run the server with an invalid configuration.

5. **Model identifiers**: An unknown model (to the project) can be used as a model identifier, but it must match a known model to reflect its known context length, which is crucial for message/token handling; e.g., `gpt-7000` will be valid but default to a 4k token limit, whereas `gpt-4-turbo` will be recognized as having a 128k context limit.

Validate your configuration against the latest schema definitions and guidelines to maintain compatibility.


### Model Deployments

The list of models available to your users are determined by the model groupings specified in your [`azureOpenAI` endpoint config.](/docs/configuration/librechat_yaml/object_structure/azure_openai)

For example:

```yaml filename="librechat.yaml"
# Example Azure OpenAI Object Structure
endpoints:
  azureOpenAI:
    groups:
      - group: "my-westus" # arbitrary name
        apiKey: "${WESTUS_API_KEY}"
        instanceName: "actual-instance-name" # name of the resource group or instance
        version: "2023-12-01-preview"
        models:
          gpt-4-vision-preview:
            deploymentName: gpt-4-vision-preview
            version: "2024-02-15-preview"
          gpt-3.5-turbo: true
      - group: "my-eastus"
        apiKey: "${EASTUS_API_KEY}"
        instanceName: "actual-eastus-instance-name"
        deploymentName: gpt-4-turbo
        version: "2024-02-15-preview"
        models:
          gpt-4-turbo: true
```

The above configuration would enable `gpt-4-vision-preview`, `gpt-3.5-turbo` and `gpt-4-turbo` for your users in the order they were defined.

### Using Assistants with Azure

To enable use of Assistants with Azure OpenAI, there are 2 main steps.

1) Set the `assistants` field, **under** the `azureOpenAI` endpoint, i.e, at the [Endpoint-level](#endpoint-level-configuration) to `true`, like so:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
  # Enable use of Assistants with Azure
    assistants: true
```

2) Add the `assistants` field to groups compatible with Azure's Assistants API integration.

- At least one of your group configurations must be compatible.
- You can check the [compatible regions and models in the Azure docs here](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#assistants-preview).
- The version must also be "2024-02-15-preview" or later, preferably later for access to the latest features.

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    assistants: true
    groups:
      - group: "my-sweden-group"
        apiKey: "${SWEDEN_API_KEY}"
        instanceName: "actual-instance-name"
      # Mark this group as assistants compatible
        assistants: true
      # version must be "2024-02-15-preview" or later
        version: "2024-03-01-preview"
        models:
          # ... (model-level configuration)
```

**Notes:**

- For credentials, rely on custom environment variables specified at each assistants-compatible group configuration.
- If you mark multiple regions as assistants-compatible, assistants you create will be aggregated across regions to the main assistant selection list.
- Files you upload to Azure OpenAI, whether at the message or assistant level, will only be available in the region the current assistant's model is part of.
    - For this reason, it's recommended you use only one region or resource group for Azure OpenAI Assistants, or you will experience an error.
    - Uploading to "OpenAI" is the default behavior for official `code_interpreter` and `retrieval` capabilities.
- Downloading files that assistants generate will soon be supported.
- As of May 19th 2024, retrieval and streaming are not yet supported through Azure OpenAI.
    - To avoid any errors with retrieval while it's not supported, it's recommended to disable the capability altogether through the `azureAssistants` endpoint config:

    ```yaml filename="librechat.yaml"
    endpoints:
      azureOpenAI:
        # ...rest

      azureAssistants:
      # "retrieval" omitted.
        capabilities: ["code_interpreter", "actions", "tools"]
    ```

    - By default, all capabilities, except retrieval, are enabled.

### Using Plugins with Azure

To use the Plugins endpoint with Azure OpenAI, you need a deployment supporting **[function calling](https://techcommunity.microsoft.com/t5/azure-ai-services-blog/function-calling-is-now-available-in-azure-openai-service/ba-p/3879241)**. Otherwise, set "Functions" off in the Agent settings. When you are not using "functions" mode, it's recommended to have "skip completion" off as well, which is a review step of what the agent generated.

To use Azure with the Plugins endpoint, make sure the field `plugins` is set to `true` in your Azure OpenAI endpoint config:

```yaml filename="librechat.yaml"
# Example Azure OpenAI Object Structure
endpoints:
  azureOpenAI:
    plugins: true # <------- Set this
    groups:
    # omitted for brevity
```

Configuring the `plugins` field will configure Plugins to use Azure models.

**NOTE**: The current configuration through `librechat.yaml` uses the primary model you select from the frontend for Plugin use, which is not usually how it works without Azure, where instead the "Agent" model is used. The Agent model setting can be ignored when using Plugins through Azure.

### Using a Specified Base URL with Azure

The base URL for Azure OpenAI API requests can be dynamically configured. This is useful for proxying services such as [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/providers/azureopenai/), or if you wish to explicitly override the baseURL handling of the app.

LibreChat will use the baseURL field for your Azure model grouping, which can include placeholders for the Azure OpenAI API instance and deployment names.

<Callout type="info" title="Azure Endpoint Domain Format Support">
Azure OpenAI now supports both endpoint domain formats:
- **New format**: `.cognitiveservices.azure.com`
- **Legacy format**: `.openai.azure.com`

When using `instanceName` without a full domain, the legacy `.openai.azure.com` format is applied by default. If you provide a full domain (e.g., `my-instance.cognitiveservices.azure.com`), it will be used as-is. This applies to both `instanceName` fields and `baseURL` configurations.
</Callout>

In the configuration, the base URL can be customized like so:

```yaml filename="librechat.yaml"
# librechat.yaml file, under an Azure group:
endpoints:
  azureOpenAI:
    groups:
      - group: "group-with-custom-base-url"
      baseURL: "https://example.azure-api.net/${INSTANCE_NAME}/${DEPLOYMENT_NAME}"

# Legacy format (.openai.azure.com)
      baseURL: "https://${INSTANCE_NAME}.openai.azure.com/openai/deployments/${DEPLOYMENT_NAME}"

# New format (.cognitiveservices.azure.com)
      baseURL: "https://${INSTANCE_NAME}.cognitiveservices.azure.com/openai/deployments/${DEPLOYMENT_NAME}"

# Cloudflare example
      baseURL: "https://gateway.ai.cloudflare.com/v1/ACCOUNT_TAG/GATEWAY/azure-openai/${INSTANCE_NAME}/${DEPLOYMENT_NAME}"
```

**NOTE**: `${INSTANCE_NAME}` and `${DEPLOYMENT_NAME}` are unique placeholders, and do not correspond to environment variables, but instead correspond to the instance and deployment name of the currently selected model. It is not recommended you use INSTANCE_NAME and DEPLOYMENT_NAME as environment variable names to avoid any potential conflicts.

**You can also omit the placeholders completely and simply construct the baseURL with your credentials:**

```yaml filename="librechat.yaml"
      baseURL: "https://gateway.ai.cloudflare.com/v1/ACCOUNT_TAG/GATEWAY/azure-openai/my-secret-instance/my-deployment"
```
**Lastly, you can specify the entire baseURL through a custom environment variable**

```yaml filename="librechat.yaml"
      baseURL: "${MY_CUSTOM_BASEURL}"
```


### Enabling Auto-Generated Titles with Azure

To enable titling for Azure, set `titleConvo` to `true`.

```yaml filename="librechat.yaml"
# Example Azure OpenAI Object Structure
endpoints:
  azureOpenAI:
    titleConvo: true # <------- Set this
    groups:
    # omitted for brevity
```

**You can also specify the model to use for titling, with `titleModel`** provided you have configured it in your group(s).

```yaml filename="titleModel"
    titleModel: "gpt-3.5-turbo"
```

**Note**: "gpt-3.5-turbo" is the default value, so you can omit it if you want to use this exact model and have it configured. If not configured and `titleConvo` is set to `true`, the titling process will result in an error and no title will be generated. You can also set this to dynamically use the current model by setting it to `current_model`.

```yaml filename="titleModel"
    titleModel: "current_model"
```

### Using GPT-4 Vision with Azure

To use Vision (image analysis) with Azure OpenAI, make sure `gpt-4-vision-preview` is a specified model [in one of your groupings](#model-deployments).

This works the same as it does with the [OpenAI endpoint](/docs/configuration/pre_configured_ai/openai): there is no need to select the vision model, as it will be switched behind the scenes.

### Generate images with Azure OpenAI Service (DALL-E)

| Model ID | Feature Availability | Max Request (characters) |
|----------|----------------------|-------------------------|
| dalle2   | East US              | 1000                    |
| dalle3   | Sweden Central       | 4000                    |

- First you need to create an Azure resource that hosts DALL-E
    - At the time of writing, dall-e-3 is available in the `SwedenCentral` region, dall-e-2 in the `EastUS` region.
- Then, you need to deploy the image generation model in one of the above regions.
    - Read the [Azure OpenAI Image Generation Quickstart Guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/dall-e-quickstart) for further assistance
- Configure your environment variables based on Azure credentials:

The DALL-E configuration options:

#### DALL-E:

**API Keys:**
<OptionTable
  options={[
    ['DALLE_API_KEY', 'string', 'The OpenAI API key for DALL-E 2 and DALL-E 3 services.','# DALLE_API_KEY='],
  ]}
/>

**API Keys (Version Specific):**
<OptionTable
  options={[
    ['DALLE3_API_KEY', 'string', 'The OpenAI API key for DALL-E 3.','# DALLE3_API_KEY='],
    ['DALLE2_API_KEY', 'string', 'The OpenAI API key for DALL-E 2.','# DALLE2_API_KEY='],
  ]}
/>

**System Prompts:**
<OptionTable
  options={[
    ['DALLE3_SYSTEM_PROMPT', 'string', 'The system prompt for DALL-E 3.','# DALLE3_SYSTEM_PROMPT="Your DALL-E-3 System Prompt here"'],
    ['DALLE2_SYSTEM_PROMPT', 'string', 'The system prompt for DALL-E 2.','# DALLE2_SYSTEM_PROMPT="Your DALL-E-2 System Prompt here"'],
  ]}
/>

**Reverse Proxy Settings:**
<OptionTable
  options={[
    ['DALLE_REVERSE_PROXY', 'string', 'The reverse proxy URL for DALL-E API requests.','# DALLE_REVERSE_PROXY='],
  ]}
/>

**Base URLs:**
<OptionTable
  options={[
    ['DALLE3_BASEURL', 'string', 'The base URL for DALL-E 3 API endpoints. Supports both `.openai.azure.com` (legacy) and `.cognitiveservices.azure.com` (new) domain formats.','# DALLE3_BASEURL=https://<AZURE_OPENAI_API_INSTANCE_NAME>.openai.azure.com/openai/deployments/<DALLE3_DEPLOYMENT_NAME>/\n# OR\n# DALLE3_BASEURL=https://<AZURE_OPENAI_API_INSTANCE_NAME>.cognitiveservices.azure.com/openai/deployments/<DALLE3_DEPLOYMENT_NAME>/'],
    ['DALLE2_BASEURL', 'string', 'The base URL for DALL-E 2 API endpoints. Supports both `.openai.azure.com` (legacy) and `.cognitiveservices.azure.com` (new) domain formats.','# DALLE2_BASEURL=https://<AZURE_OPENAI_API_INSTANCE_NAME>.openai.azure.com/openai/deployments/<DALLE2_DEPLOYMENT_NAME>/\n# OR\n# DALLE2_BASEURL=https://<AZURE_OPENAI_API_INSTANCE_NAME>.cognitiveservices.azure.com/openai/deployments/<DALLE2_DEPLOYMENT_NAME>/'],
  ]}
/>

**Azure OpenAI Integration (Optional):**
<OptionTable
  options={[
    ['DALLE3_AZURE_API_VERSION', 'string', 'The API version for DALL-E 3 with Azure OpenAI service.','# DALLE3_AZURE_API_VERSION=the-api-version # e.g.: 2023-12-01-preview'],
    ['DALLE2_AZURE_API_VERSION', 'string', 'The API version for DALL-E 2 with Azure OpenAI service.','# DALLE2_AZURE_API_VERSION=the-api-version # e.g.: 2023-12-01-preview'],
  ]}
/>

Replace placeholder text with actual prompts or instructions, and provide your API keys if you choose to include them directly in the file (managing sensitive keys outside of the codebase is recommended). Review and respect OpenAI's usage policies when embedding API keys in software.

> Note: if you have PROXY set, it will be used for DALL-E calls also, which is universal for the app.

### Serverless Inference Endpoints

Through the `librechat.yaml` file, you can configure Azure AI Studio serverless inference endpoints to access models from the [Azure AI Foundry.](https://ai.azure.com/explore) Only a model identifier, `baseURL`, and `apiKey` are needed along with the `serverless` field to indicate the special handling these endpoints need.

- You will need to follow the instructions in the compatible model cards to set up **MaaS** ("Models as a Service") access on Azure AI Studio.

    - For reference, here are some known compatible model cards:

    - [Mistral-large](https://aka.ms/aistudio/landing/mistral-large) | [Meta-Llama-3.1-8B-Instruct](https://ai.azure.com/explore/models/Meta-Llama-3.1-8B-Instruct/version/4/) | [Phi-3-medium-128k-instruct](https://ai.azure.com/explore/models/Phi-3-medium-128k-instruct/version/1/registry/azureml)

- You can also review [the technical blog for the "Mistral-large" model release](https://techcommunity.microsoft.com/t5/ai-machine-learning-blog/mistral-large-mistral-ai-s-flagship-llm-debuts-on-azure-ai/ba-p/4066996) for more info.

- Then, you will need to add them to your `azureOpenAI` config in the librechat.yaml file.

- Here is an example configuration for `Meta-Llama-3.1-8B-Instruct`:

```yaml filename="librechat.yaml"
endpoints:
  azureOpenAI:
    groups:
    - group: "serverless-example"
      apiKey: "${LLAMA318B_API_KEY}"  # arbitrary env var name
      baseURL: "https://example.services.ai.azure.com/models/"
      version: "2024-05-01-preview" # Optional: specify API version
      serverless: true
      models:
        # Must match the deployment name of the model
        Meta-Llama-3.1-8B-Instruct: true
```

**Notes**:

- Azure AI Foundry models now provision endpoints under `/models/chat/completions?api-version=version` for serverless inference.
  - The `baseURL` field should be set to the root of the endpoint, without anything after `/models/`, i.e., the `/chat/completions` path.
  - Example: `https://example.services.ai.azure.com/models/` for `https://example.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview`
  - The `version` query parameter is optional and can be specified in the `baseURL` field.
- The model name used in the `models` field must match the deployment name of the model in the Azure AI Foundry. 
- Compatibility with LibreChat relies on parity with OpenAI API specs, which at the time of writing, are typically **"Pay-as-you-go"** or "Models as a Service" (MaaS) deployments on Azure AI Studio, that are OpenAI-SDK-compatible with either `v1/completions` or `models/chat/completions` endpoint handling.
- All models that offer serverless deployments ("Serverless APIs") are compatible from the Azure model catalog. You can filter by "Serverless API" under Deployment options and "Chat completion" under inference tasks to see the full list; however, real time endpoint models have not been tested.
- These serverless inference endpoint/models may or may not support function calling according to OpenAI API specs, which enables their use with Agents.


# Cloudflare Workers AI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/cloudflare)

Cloudflare Workers AI runs open models on Cloudflare's network, and its AI Gateway exposes an OpenAI-compatible endpoint scoped to your account and gateway.

## Get an API key

Create an AI Gateway by following the [AI Gateway setup guide](https://developers.cloudflare.com/ai-gateway/get-started/), then add your credentials to your `.env` file:

```bash filename=".env"
CF_API_TOKEN=your-api-key
CF_ACCOUNT_ID=your-account-id
CF_GATEWAY_ID=your-gateway-id
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`. The `baseURL` embeds your account ID and gateway ID:

```yaml filename="librechat.yaml"
    - name: "Cloudflare Workers AI"
      apiKey: "${CF_API_TOKEN}"
      baseURL: "https://gateway.ai.cloudflare.com/v1/${CF_ACCOUNT_ID}/${CF_GATEWAY_ID}/workers-ai/v1"
      models:
        default: [
          "@cf/google/gemma-3-12b-it",
          "@cf/meta/llama-4-scout-17b-16e-instruct",
          "@cf/qwen/qwq-32b",
          "@cf/qwen/qwen2.5-coder-32b-instruct",
          "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
          "@cf/openai/gpt-oss-120b"
        ]
        fetch: false
      titleConvo: true
      titleModel: "@cf/google/gemma-3-12b-it"
      modelDisplayLabel: "Cloudflare AI"
```

## Notes

- This uses the [OpenAI-compatible endpoint](https://developers.cloudflare.com/ai-gateway/providers/workersai/#openai-compatible-endpoints) of Workers AI. Browse available models in the [Workers AI model catalog](https://developers.cloudflare.com/workers-ai/models/).
- `@cf/openai/gpt-oss-*` models may not work due to spec incompatibility.
- The `CF_API_TOKEN`, `CF_ACCOUNT_ID`, and `CF_GATEWAY_ID` variable names can be renamed as long as they match between your `.env` and `librechat.yaml`.


# Cohere (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/cohere)

Cohere provides the Command family of models, used in LibreChat as a custom endpoint. Its API does not follow the OpenAI spec, so it relies on a compatibility layer that maps a subset of parameters.

## Get an API key

Create a key from the [Cohere dashboard](https://dashboard.cohere.com/). Add it to your `.env` file:

```bash filename=".env"
COHERE_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "cohere"
      apiKey: "${COHERE_API_KEY}"
      baseURL: "https://api.cohere.ai/v1"
      models:
        default: ["command-r","command-r-plus","command-light","command-light-nightly","command","command-nightly"]
        fetch: false
      modelDisplayLabel: "cohere"
      titleModel: "command"
      dropParams: ["stop", "user", "frequency_penalty", "presence_penalty", "temperature", "top_p"]
```

## Notes

- Cohere does not follow the OpenAI spec. A compatibility layer maps a subset of OpenAI parameters to Cohere's equivalents: `stop` to `stopSequences`, `top_p` to `p`, `frequency_penalty` to `frequencyPenalty`, and `presence_penalty` to `presencePenalty` (each with different min/max ranges). `model` and `stream` are shared and sent by default; `max_tokens` maps to `maxTokens` but is not sent by default. The example above uses `dropParams` to remove most of these and fall back to Cohere's defaults.
- For the full list of Cohere-specific parameters, see the [Cohere API documentation](https://docs.cohere.com/reference/chat).


# Databricks (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/databricks)

Databricks serves foundation models and your own fine-tuned models through Mosaic AI Model Serving, which exposes an OpenAI-compatible serving endpoint.

## Get an API key

[Sign up for Databricks](https://www.databricks.com/try-databricks#account) and generate a personal access token from your workspace. Add it to your `.env` file:

```bash filename=".env"
DATABRICKS_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: 'Databricks'
      apiKey: '${DATABRICKS_API_KEY}'
      baseURL: 'https://your_databricks_serving_endpoint_url_here_ending_with/invocations'
      models:
        default: [
          "databricks-meta-llama-3-70b-instruct",
        ]
        fetch: false
      titleConvo: true
      titleModel: 'current_model'
      directEndpoint: true # required
      titleMessageRole: 'user' # required
```

## Notes

- Databricks exposes a full completions endpoint ending in `invocations` for `serving-endpoints`, so [directEndpoint](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#directendpoint) is required.
- Set [titleMessageRole](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#titlemessagerole) to `user` for title generation. A standalone `system` message is not supported.


# Deepseek (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/deepseek)

import Image from 'next/image'

Deepseek serves its chat, coder, and reasoner models through an OpenAI-compatible API that you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Deepseek platform](https://platform.deepseek.com/usage). Add it to your `.env` file:

```bash filename=".env"
DEEPSEEK_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Deepseek"
      apiKey: "${DEEPSEEK_API_KEY}"
      baseURL: "https://api.deepseek.com/v1"
      models:
        default: ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"]
        fetch: false
      titleConvo: true
      titleModel: "deepseek-chat"
      modelDisplayLabel: "Deepseek"
```

## Notes

- `deepseek-chat` and `deepseek-coder` work with [Agents and tools](/docs/features/agents).
- `deepseek-reasoner`, codenamed "R1," is supported and streams its thought process, but some OpenAI API parameters may not work with it. R1 can also work with Agents when tools are not used.
- `deepseek-chat` is the preferred model for title generation.

<Image src="https://firebasestorage.googleapis.com/v0/b/superb-reporter-407417.appspot.com/o/chrome_GsVGKQ8aF3.png?alt=media&token=30cde5cd-3b62-428a-bd24-b58afff0e4bb" alt="Deepseek Generation" width={943} height={747}/>


# Fireworks (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/fireworks)

Fireworks AI serves a range of open models through an OpenAI-compatible API, used in LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Fireworks API keys page](https://fireworks.ai/api-keys). Add it to your `.env` file:

```bash filename=".env"
FIREWORKS_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Fireworks"
      apiKey: "${FIREWORKS_API_KEY}"
      baseURL: "https://api.fireworks.ai/inference/v1"
      models:
        default: [
          "accounts/fireworks/models/mixtral-8x7b-instruct",
          ]
        fetch: true
      titleConvo: true
      titleModel: "accounts/fireworks/models/llama-v2-7b-chat"
      summarize: false
      summaryModel: "accounts/fireworks/models/llama-v2-7b-chat"
      modelDisplayLabel: "Fireworks"
      dropParams: ["user"]
```

## Notes

- The API is strict for some models and may reject fields like `user`, so drop them with [`dropParams`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#dropparams).


# Groq (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/groq)

Groq runs open models such as Llama and Mixtral on its LPU inference hardware, exposed through an OpenAI-compatible API that you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Groq console](https://console.groq.com/keys). Add it to your `.env` file:

```bash filename=".env"
GROQ_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "groq"
      apiKey: "${GROQ_API_KEY}"
      baseURL: "https://api.groq.com/openai/v1/"
      models:
        default: [
          "llama3-70b-8192",
          "llama3-8b-8192",
          "llama2-70b-4096",
          "mixtral-8x7b-32768",
          "gemma-7b-it",
          ]
        fetch: false
      titleConvo: true
      titleModel: "mixtral-8x7b-32768"
      modelDisplayLabel: "groq"
```

## Notes

- A temperature of `0` is converted to `1e-8`. If you hit issues, use a float greater than 0 and up to 2.
- Groq is free but rate limited to 10 queries per minute and 100 per hour.


# Helicone (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/helicone)

Helicone is an AI gateway that routes requests to models from OpenAI, Anthropic, Google, Meta, Mistral, and other providers through a single OpenAI-compatible endpoint, with built-in request logging and usage analytics.

## Get an API key

Create an account at [helicone.ai](https://helicone.ai/) and generate a key from [Settings → API Keys](https://us.helicone.ai/settings/api-keys). Add it to your `.env` file:

```bash filename=".env"
HELICONE_KEY=your-api-key
```

Make sure your account has credits, otherwise model requests will fail.

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Helicone"
      apiKey: "${HELICONE_KEY}"
      baseURL: "https://ai-gateway.helicone.ai"
      headers:
        x-librechat-body-parentmessageid: "{{LIBRECHAT_BODY_PARENTMESSAGEID}}"
      models:
        default: ["gpt-4o-mini", "claude-4.5-sonnet", "llama-3.1-8b-instruct", "gemini-2.5-flash-lite"]
        fetch: true
      titleConvo: true
      titleModel: "gpt-4o-mini"
      modelDisplayLabel: "Helicone"
      iconURL: "https://marketing-assets-helicone.s3.us-west-2.amazonaws.com/helicone.png"
```

## Notes

- With `fetch: true`, LibreChat loads the full model list from Helicone, so the `default` array is only the initial selection. Browse everything available in the [Helicone model library](https://helicone.ai/models).
- The `x-librechat-body-parentmessageid` header passes each request's parent message ID to Helicone so logs can be grouped by conversation.
- Set rate limits, caching policies, and review usage from the [Helicone dashboard](https://us.helicone.ai/dashboard).


# Huggingface (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/huggingface)

Huggingface exposes hosted models through an OpenAI-compatible inference API, which you can add to LibreChat as a custom endpoint.

## Get an API key

Create a token at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens). Add it to your `.env` file:

```bash filename=".env"
HUGGINGFACE_TOKEN=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: 'HuggingFace'
      apiKey: '${HUGGINGFACE_TOKEN}'
      baseURL: 'https://api-inference.huggingface.co/v1'
      models:
        default: [
          "codellama/CodeLlama-34b-Instruct-hf",
          "google/gemma-1.1-2b-it",
          "google/gemma-1.1-7b-it",
          "HuggingFaceH4/starchat2-15b-v0.1",
          "HuggingFaceH4/zephyr-7b-beta",
          "meta-llama/Meta-Llama-3-8B-Instruct",
          "microsoft/Phi-3-mini-4k-instruct",
          "mistralai/Mistral-7B-Instruct-v0.1",
          "mistralai/Mistral-7B-Instruct-v0.2",
          "mistralai/Mixtral-8x7B-Instruct-v0.1",
          "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
        ]
        fetch: true
      titleConvo: true
      titleModel: "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO"
      dropParams: ["top_p"]
      modelDisplayLabel: "HuggingFace"
```

The model list above was last updated on May 09, 2024.

## Notes

- The listed models are free but rate limited, and answers can be very short on the free tier. Some models work better than others.
- Fetching the model list is not supported, so set the `default` array yourself.
- `dropParams: ["top_p"]` is required. Without it, requests fail because Huggingface rejects the `top_p` parameter. See [`dropParams`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#dropparams).

<Callout type="warning" title="Other Model Errors" collapsible>
    Here's a list of the other models that were tested along with their corresponding errors

    ```yaml
      models:
        default: [
          "CohereForAI/c4ai-command-r-plus", # Model requires a Pro subscription
          "HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1", # Model requires a Pro subscription
          "meta-llama/Llama-2-7b-hf", # Model requires a Pro subscription
          "meta-llama/Meta-Llama-3-70B-Instruct", # Model requires a Pro subscription
          "meta-llama/Llama-2-13b-chat-hf", # Model requires a Pro subscription
          "meta-llama/Llama-2-13b-hf", # Model requires a Pro subscription
          "meta-llama/Llama-2-70b-chat-hf", # Model requires a Pro subscription
          "meta-llama/Llama-2-7b-chat-hf", # Model requires a Pro subscription
          "------",
          "bigcode/octocoder", # template not found
          "bigcode/santacoder", # template not found
          "bigcode/starcoder2-15b", # template not found
          "bigcode/starcoder2-3b", # template not found 
          "codellama/CodeLlama-13b-hf", # template not found
          "codellama/CodeLlama-7b-hf", # template not found
          "google/gemma-2b", # template not found
          "google/gemma-7b", # template not found
          "HuggingFaceH4/starchat-beta", # template not found
          "HuggingFaceM4/idefics-80b-instruct", # template not found
          "HuggingFaceM4/idefics-9b-instruct", # template not found
          "HuggingFaceM4/idefics2-8b", # template not found
          "kashif/stack-llama-2", # template not found
          "lvwerra/starcoderbase-gsm8k", # template not found
          "tiiuae/falcon-7b", # template not found
          "timdettmers/guanaco-33b-merged", # template not found
          "------",
          "bigscience/bloom", # 404 status code (no body)
          "------",
          "google/gemma-2b-it", # stream` is not supported for this model / unknown error
          "------",
          "google/gemma-7b-it", # AI Response error likely caused by Google censor/filter
          "------",
          "bigcode/starcoder", # Service Unavailable
          "google/flan-t5-xxl", # Service Unavailable
          "HuggingFaceH4/zephyr-7b-alpha", # Service Unavailable
          "mistralai/Mistral-7B-v0.1", # Service Unavailable
          "OpenAssistant/oasst-sft-1-pythia-12b", # Service Unavailable
          "OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5", # Service Unavailable
        ]
    ```
</Callout>


# LiteLLM (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/litellm)

LiteLLM Proxy is a self-hosted gateway that exposes models from many providers through a single OpenAI-compatible endpoint, so you can point LibreChat at your own proxy.

## Configuration

The API key matches the value set in your LiteLLM proxy config rather than a provider key, so use the placeholder below or your own. Point `baseURL` at your running proxy. Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "LiteLLM"
      apiKey: "sk-from-config-file"
      baseURL: "http://localhost:8000/v1"
      # if using LiteLLM example in docker-compose.override.yml.example, use "http://litellm:8000/v1"
      models:
        default: ["gpt-3.5-turbo"]
        fetch: true
      titleConvo: true
      titleModel: "gpt-3.5-turbo"
      summarize: false
      summaryModel: "gpt-3.5-turbo"
      modelDisplayLabel: "LiteLLM"
```

## Notes

- See [Using LibreChat with LiteLLM Proxy](/blog/2023-11-30_litellm) for a full walkthrough of setting up the proxy.
- With `fetch: true`, LibreChat loads the full list of models configured in your proxy, so `default` is only the initial selection.
- If you run the bundled proxy from `docker-compose.override.yml.example`, set `baseURL` to `http://litellm:8000/v1` so the containers reach each other by service name.


# Mistral (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/mistral)

Mistral provides its own family of chat and embedding models, used in LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Mistral console](https://console.mistral.ai/). Add it to your `.env` file:

```bash filename=".env"
MISTRAL_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Mistral"
      apiKey: "${MISTRAL_API_KEY}"
      baseURL: "https://api.mistral.ai/v1"
      models:
        default: ["mistral-tiny", "mistral-small", "mistral-medium", "mistral-large-latest"]
        fetch: true
      titleConvo: true
      titleModel: "mistral-tiny"
      modelDisplayLabel: "Mistral"
      dropParams: ["stop", "user", "frequency_penalty", "presence_penalty"]
```

## Notes

- The Mistral API only allows a system message at the top of the messages payload.
- The API is strict with unrecognized parameters and its errors are not descriptive (often just "no body"). Using [`dropParams`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#dropparams) to drop `user`, `frequency_penalty`, and `presence_penalty` is required.
- `stop` is no longer a default parameter, so it does not need to be in [`dropParams`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#dropparams) unless you want to prevent users from configuring it.
- Fetching the model list is supported, but be careful not to select embedding models for chat.


# Apple MLX (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/mlx)

Apple MLX serves models locally on Apple silicon through an [OpenAI-compatible API](https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/SERVER.md), so you can point LibreChat at your own machine.

## Configuration

The local MLX server doesn't authenticate requests, so the API key is just a placeholder. Point `baseURL` at your running server. Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "MLX"
      apiKey: "mlx"
      baseURL: "http://localhost:8080/v1/" 
      models:
        default: [
          "Meta-Llama-3-8B-Instruct-4bit"
          ]
        fetch: false # fetching list of models is not supported
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "Apple MLX"
      addParams:
            max_tokens: 2000
            "stop": [
              "<|eot_id|>"
            ]
```

## Notes

- The MLX server runs one model at a time. To serve more than one model, run a separate instance on a different port and add another endpoint with its own `baseURL`.
- The API is strict about unrecognized parameters, so keep `addParams` limited to values the server accepts, such as `max_tokens` and `stop`.


# Moonshot (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/moonshot)

Moonshot AI serves its Kimi models through an OpenAI-compatible API that you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Moonshot platform](https://platform.moonshot.ai). Add it to your `.env` file:

```bash filename=".env"
MOONSHOT_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Moonshot"
      apiKey: "${MOONSHOT_API_KEY}"
      baseURL: "https://api.moonshot.ai/v1"
      models:
        default: ["kimi-k2.5"]
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      modelDisplayLabel: "Moonshot"
```

## Notes

- For models with reasoning capabilities such as `kimi-k2.5` and `kimi-k2-thinking`, the endpoint `name` must be set to `"Moonshot"` (case-insensitive) for interleaved reasoning to work with tool calls. A different name causes errors like `thinking is enabled but reasoning_content is missing in assistant tool call message`. See [Moonshot's documentation](https://platform.moonshot.ai/docs/guide/use-kimi-k2-thinking-model#frequently-asked-questions) for details.


# NEAR AI Cloud (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/nearai)

NEAR AI Cloud provides an OpenAI-compatible endpoint that you can add to LibreChat through `endpoints.custom`.

## Get an API key

Create an API key from [NEAR AI Cloud](https://cloud.near.ai/). Add it to your `.env` file:

```bash filename=".env"
NEARAI_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "nearai"
      apiKey: "${NEARAI_API_KEY}"
      baseURL: "https://cloud-api.near.ai/v1"
      models:
        default:
          - "z-ai/glm-5.2"
          - "deepseek-ai/DeepSeek-V4-Flash"
        fetch: true
      titleConvo: true
      titleModel: "z-ai/glm-5.2"
      modelDisplayLabel: "NEAR AI Cloud"
```

To let each user supply their own key through the LibreChat UI instead of reading one from `.env`, set `apiKey: "user_provided"`.

## Notes

- With `fetch: true`, LibreChat loads the model list from NEAR AI Cloud's OpenAI-compatible `/v1/models` endpoint. The `default` array is only the initial selection.
- Use model IDs exactly as they appear in the [NEAR AI Cloud model catalog](https://cloud-api.near.ai/v1/model/list).
- Model availability can change over time. If a selected model is unavailable, choose another chat-capable model from the catalog.


# NeurochainAI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/neurochain)

NeurochainAI is an OpenAI-compatible inference network. Replace `<=generated api key=>` below with the key you create in the [NeurochainAI REST API documentation](https://app.neurochain.ai/network-integrations/rest-api).

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "NeurochainAI"
      apiKey: "<=generated api key=>"
      baseURL: "https://ncmb.neurochain.io/v1/"
      models:
        default: [
          "Mistral-7B-OpenOrca-GPTQ"
        ]
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "NeurochainAI"
      iconURL: "https://raw.githubusercontent.com/LibreChat-AI/librechat-config-yaml/refs/heads/main/icons/NeurochainAI.png"
```

## Notes

- The model list grows over time, so the example may be outdated. Check [NeurochainAI](https://neurochain.ai) for the latest models.


# Ollama (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/ollama)

Ollama runs open models locally and exposes an OpenAI-compatible API, so you can point LibreChat at your own machine. Download models with `ollama run <model>` and browse what's available in the [Ollama Library](https://ollama.com/library).

## Configuration

Ollama ignores the API key but still expects the field to be present, so set it to any placeholder. Point `baseURL` at your Ollama server. Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Ollama"
      apiKey: "ollama"
      # use 'host.docker.internal' instead of localhost if running LibreChat in a docker container
      baseURL: "http://localhost:11434/v1/" 
      models:
        default: [
          "llama2",
          "mistral",
          "codellama",
          "dolphin-mixtral",
          "mistral-openorca"
          ]
        # fetching list of models is supported but the `name` field must start
        # with `ollama` (case-insensitive), as it does in this example.
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "Ollama"
```

## Notes

- Set `titleModel` to `"current_model"` so title generation reuses the conversation's model instead of loading a second one. This keeps Ollama to a single loaded model per conversation.
- The `default` array above is a sample list of popular models. With `fetch: true`, LibreChat pulls the full list from your server.

<Callout type="tip" title="Ollama -> llama3">

Once `stop` was removed from the [default parameters](/docs/configuration/librechat_yaml/object_structure/default_params), the issue below should no longer occur.

If `llama3` keeps generating without stopping, add an `addParams` block with the stop sequences:

```yaml filename="librechat.yaml"
    - name: "Ollama"
      apiKey: "ollama"
      baseURL: "http://host.docker.internal:11434/v1/"
      models:
        default: [
          "llama3"
        ]
        fetch: false # pinned to the list above; set true to discover models from the server
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "Ollama"
      addParams:
          "stop": [
              "<|start_header_id|>",
              "<|end_header_id|>",
              "<|eot_id|>",
              "<|reserved_special_token"
          ]
```

If you only run `llama3` with Ollama, setting `stop` at the config level via `addParams` is fine. When you run several models, add stop sequences from the frontend through conversation parameters and presets instead, and omit `addParams`:

```yaml filename="librechat.yaml"
    - name: "Ollama"
      apiKey: "ollama"
      baseURL: "http://host.docker.internal:11434/v1/" 
      models:
        default: [
          "llama3:latest",
          "mistral"
          ]
        fetch: false # pinned to the list above; set true to discover models from the server
      titleConvo: true
      titleModel: "current_model"
      modelDisplayLabel: "Ollama"
```

Set the stop sequences in conversation parameters (and save them as a preset). Open a conversation on the Ollama endpoint, open the right-hand parameters panel, and add each sequence under **Stop Sequences**:

![LibreChat conversation parameters panel with four llama3 stop sequences entered in the Stop Sequences field](https://github.com/danny-avila/LibreChat/assets/110412045/57460b8c-308a-4d21-9dfe-f48a2ac85099)

</Callout>

## Troubleshooting

### Ollama does not appear, or the model list is empty

Work through these in order:

1. **Check the endpoint is reachable from LibreChat, not from your shell.** If LibreChat runs in Docker, `localhost` is the API container itself, not your host. Use `http://host.docker.internal:11434/v1/` on Docker Desktop, or the host's LAN address on Linux where `host.docker.internal` may be unavailable. Running LibreChat outside Docker is the only case where `http://localhost:11434/v1/` is correct.
2. **Confirm Ollama is listening beyond loopback.** By default Ollama binds to `127.0.0.1`, which a container cannot reach. Set `OLLAMA_HOST=0.0.0.0` in Ollama's own environment and restart it.

   <Callout type="warning" title="Binding to 0.0.0.0 exposes Ollama on every interface">
   Ollama's API is unauthenticated. Binding it to `0.0.0.0` on a machine with a LAN or public interface hands model access, and the ability to pull and delete models, to anyone who can reach port 11434. Prefer binding to just the address the LibreChat container actually reaches, which is the gateway of its compose network (`docker network inspect <network>` reports it), and firewall port 11434 so nothing else can reach it.
   </Callout>
3. **Know what the endpoint name changes.** LibreChat reaches for Ollama's native `/api/tags` only when the endpoint `name` starts with `ollama`, case-insensitively. Any other name, or a failure of that native call, falls through to the generic OpenAI-compatible `/v1/models` request. Current Ollama versions answer that one too at the `/v1/` base URL above, so a renamed endpoint usually still returns a model list. The prefix matters when you specifically need the native tags route, and a hosted proxy that serves `/v1/models` but not `/api/tags` is better off without it.
4. **Set `apiKey` to any non-empty placeholder.** Ollama ignores the value, but a custom endpoint with no `apiKey` is dropped at config load.
5. **Read the API logs.** `docker compose logs api` reports the connection error and the URL it actually tried.

### Using a remote or hosted Ollama server

Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and put the credential in `apiKey` instead of the placeholder. Name it whatever you like: model fetching falls back to the OpenAI-compatible `/v1/models` route, which is usually what a hosted proxy exposes.

`apiKey` is only ever sent as `Authorization: Bearer <key>`, and only when your `headers` block has not already set an `Authorization` header. If your hosted proxy expects a different scheme, such as `X-API-Key` or Basic auth, put the real credential in `headers`: an `Authorization` entry there replaces the Bearer fallback, and any other header is sent alongside it. `apiKey` still has to be non-empty either way, because an endpoint without one is dropped at config load.

```yaml filename="excerpt of librechat.yaml"
- name: "Ollama"
  apiKey: "unused"
  baseURL: "https://ollama.example.com/v1/"
  headers:
    X-API-Key: "${OLLAMA_PROXY_KEY}"
  models:
    default: ["llama3:latest"]
    fetch: true
```


# OpenRouter (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/openrouter)

OpenRouter routes requests to hundreds of models from OpenAI, Anthropic, Google, Meta, Mistral, and other providers through a single OpenAI-compatible endpoint.

## Get an API key

Create an account at [openrouter.ai](https://openrouter.ai/) and generate a key from the [Keys page](https://openrouter.ai/keys). The key starts with `sk-or-v1-`. Add it to your `.env` file:

```bash filename=".env"
OPENROUTER_KEY=sk-or-v1-your-key-here
```

<Callout type="error" title="Use OPENROUTER_KEY, not OPENROUTER_API_KEY">

The variable must be named `OPENROUTER_KEY`. Naming it `OPENROUTER_API_KEY` reroutes the built-in OpenAI endpoint through OpenRouter as well, which is almost never what you want.

</Callout>

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "OpenRouter"
      apiKey: "${OPENROUTER_KEY}"
      baseURL: "https://openrouter.ai/api/v1"
      models:
        default: ["meta-llama/llama-3-70b-instruct"]
        fetch: true
      titleConvo: true
      titleModel: "meta-llama/llama-3-70b-instruct"
      dropParams: ["stop"]
      modelDisplayLabel: "OpenRouter"
```

To pin a fixed model list instead of fetching the full catalog, set `fetch: false` and list the models yourself:

```yaml filename="librechat.yaml"
      models:
        default: ["anthropic/claude-3.5-sonnet", "openai/gpt-4o", "meta-llama/llama-3-70b-instruct"]
        fetch: false
```

To let each user supply their own key through the LibreChat UI instead of reading one from `.env`, set `apiKey: "user_provided"`. Users then see a key input field when they select the endpoint.

## Notes

- With `fetch: true`, LibreChat loads the full model list from OpenRouter so new models appear automatically. The `default` array is only the initial selection.
- `dropParams: ["stop"]` strips the `stop` parameter from requests. OpenRouter models use varied stop tokens, and dropping it avoids compatibility errors.
- A `402 Payment Required` response comes from OpenRouter, not LibreChat. Add credits or pick a free model in your OpenRouter account, then retry.


# Perplexity (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/perplexity)

Perplexity provides the Sonar family of search-grounded models, used in LibreChat as a custom endpoint.

## Get an API key

Create a key from the [Perplexity API settings](https://www.perplexity.ai/settings/api). Add it to your `.env` file:

```bash filename=".env"
PERPLEXITY_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "Perplexity"
      apiKey: "${PERPLEXITY_API_KEY}"
      baseURL: "https://api.perplexity.ai/"
      models:
        default: [
          "sonar-deep-research",
          "sonar-reasoning-pro",
          "sonar-reasoning",
          "sonar-pro",
          "sonar",
          "r1-1776"
          ]
        fetch: false
      titleConvo: true
      titleModel: "llama-3-sonar-small-32k-chat"
      summarize: false
      summaryModel: "llama-3-sonar-small-32k-chat"
      dropParams: ["stop", "frequency_penalty"]
      modelDisplayLabel: "Perplexity"
```

## Notes

- Fetching the model list is not supported, so keep `fetch: false` and maintain the `default` list manually.
- The API is strict for some models. Fields like `stop` and `frequency_penalty` can cause an error when set to 0, so drop them with [`dropParams`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#dropparams).


# Portkey AI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/portkey)

Portkey is an AI gateway that fronts 250+ models through an OpenAI-compatible endpoint, adding observability, 50+ guardrails, caching, and conditional routing with fallbacks and retries. See the full provider list in the [Portkey docs](https://docs.portkey.ai/docs/integrations/llms).

## Get an API key

Create a key at [app.portkey.ai](https://app.portkey.ai/) and add it to your `.env` file. You will also need a gateway URL, which Portkey provides for self-hosted or hosted gateways:

```bash filename=".env"
PORTKEY_API_KEY=your-api-key
PORTKEY_GATEWAY_URL=your-gateway-url
```

## Configuration

LibreChat requires the `apiKey` field, but Portkey does not use it, so pass the string `dummy`. Authentication happens through the `x-portkey-*` headers instead. There are two ways to connect, depending on whether you route by [Virtual Keys](https://docs.portkey.ai/docs/product/ai-gateway/virtual-keys) or [Configs](https://docs.portkey.ai/docs/product/ai-gateway/configs). Add one of the following under `endpoints.custom` in your `librechat.yaml`.

### Virtual Keys

```yaml filename="librechat.yaml"
    - name: "Portkey"
      apiKey: "dummy"
      baseURL: ${PORTKEY_GATEWAY_URL}
      headers:
        x-portkey-api-key: "${PORTKEY_API_KEY}"
        x-portkey-virtual-key: "PORTKEY_OPENAI_VIRTUAL_KEY"
      models:
        default: ["gpt-4o-mini"]
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "Portkey:OpenAI"
      iconURL: https://images.crunchbase.com/image/upload/c_pad,f_auto,q_auto:eco,dpr_1/rjqy7ghvjoiu4cd1xjbf
```

### Configs

```yaml filename="librechat.yaml"
    - name: "Portkey"
      apiKey: "dummy"
      baseURL: ${PORTKEY_GATEWAY_URL}
      headers:
        x-portkey-api-key: "${PORTKEY_API_KEY}"
        x-portkey-config: "pc-libre-xxx"
      models:
        default: ["llama-3.2"]
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "Portkey:Llama"
      iconURL: https://images.crunchbase.com/image/upload/c_pad,f_auto,q_auto:eco,dpr_1/rjqy7ghvjoiu4cd1xjbf
```

## Notes

- Configs let you set model-specific parameters like `top_p` and `max_tokens` on the Portkey side. See the [Configs docs](https://docs.portkey.ai/docs/product/ai-gateway/configs#configs).
- Replace `PORTKEY_OPENAI_VIRTUAL_KEY` and `pc-libre-xxx` with the virtual key or config ID from your Portkey dashboard.


# ShuttleAI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/shuttleai)

ShuttleAI provides access to its Shuttle models through an OpenAI-compatible API, used in LibreChat as a custom endpoint.

## Get an API key

Create a key from the [ShuttleAI keys page](https://shuttleai.com/keys). Add it to your `.env` file:

```bash filename=".env"
SHUTTLEAI_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "ShuttleAI"
      apiKey: "${SHUTTLEAI_API_KEY}"
      baseURL: "https://api.shuttleai.com/v1"
      models:
        default: [
          "shuttle-2.5", "shuttle-2.5-mini"
          ]
        fetch: true
      titleConvo: true
      titleModel: "shuttle-2.5-mini"
      summarize: false
      summaryModel: "shuttle-2.5-mini"
      modelDisplayLabel: "ShuttleAI"
      dropParams: ["user", "stop"]
```


# together.ai (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/togetherai)

together.ai serves a large catalog of open models through an OpenAI-compatible API, which you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key at [api.together.xyz/settings/api-keys](https://api.together.xyz/settings/api-keys). Add it to your `.env` file:

```bash filename=".env"
TOGETHERAI_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "together.ai"
      apiKey: "${TOGETHERAI_API_KEY}"
      baseURL: "https://api.together.xyz"
      models:
        default: [
          "Austism/chronos-hermes-13b",
          "Gryphe/MythoMax-L2-13b",
          "HuggingFaceH4/zephyr-7b-beta",
          "NousResearch/Hermes-2-Theta-Llama-3-70B",
          "NousResearch/Nous-Capybara-7B-V1p9",
          "NousResearch/Nous-Hermes-2-Mistral-7B-DPO",
          "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
          "NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT",
          "NousResearch/Nous-Hermes-2-Yi-34B",
          "NousResearch/Nous-Hermes-Llama2-13b",
          "NousResearch/Nous-Hermes-Llama2-70b",
          "NousResearch/Nous-Hermes-llama-2-7b",
          "Open-Orca/Mistral-7B-OpenOrca",
          "Qwen/Qwen1.5-0.5B-Chat",
          "Qwen/Qwen1.5-1.8B-Chat",
          "Qwen/Qwen1.5-110B-Chat",
          "Qwen/Qwen1.5-14B-Chat",
          "Qwen/Qwen1.5-32B-Chat",
          "Qwen/Qwen1.5-4B-Chat",
          "Qwen/Qwen1.5-72B-Chat",
          "Qwen/Qwen1.5-7B-Chat",
          "Qwen/Qwen2-1.5B",
          "Qwen/Qwen2-1.5B-Instruct",
          "Qwen/Qwen2-72B",
          "Qwen/Qwen2-72B-Instruct",
          "Qwen/Qwen2-7B",
          "Qwen/Qwen2-7B-Instruct",
          "Snowflake/snowflake-arctic-instruct",
          "Undi95/ReMM-SLERP-L2-13B",
          "Undi95/Toppy-M-7B",
          "WizardLM/WizardLM-13B-V1.2",
          "allenai/OLMo-7B-Instruct",
          "carson/ml31405bit",
          "carson/ml3170bit",
          "carson/ml318bit",
          "carson/ml318br",
          "codellama/CodeLlama-13b-Instruct-hf",
          "codellama/CodeLlama-34b-Instruct-hf",
          "codellama/CodeLlama-70b-Instruct-hf",
          "codellama/CodeLlama-7b-Instruct-hf",
          "cognitivecomputations/dolphin-2.5-mixtral-8x7b",
          "databricks/dbrx-instruct",
          "deepseek-ai/deepseek-coder-33b-instruct",
          "deepseek-ai/deepseek-llm-67b-chat",
          "garage-bAInd/Platypus2-70B-instruct",
          "google/gemma-2-27b-it",
          "google/gemma-2-9b-it",
          "google/gemma-2b-it",
          "google/gemma-7b-it",
          "gradientai/Llama-3-70B-Instruct-Gradient-1048k",
          "lmsys/vicuna-13b-v1.3",
          "lmsys/vicuna-13b-v1.5",
          "lmsys/vicuna-13b-v1.5-16k",
          "lmsys/vicuna-7b-v1.3",
          "lmsys/vicuna-7b-v1.5",
          "meta-llama/Llama-2-13b-chat-hf",
          "meta-llama/Llama-2-70b-chat-hf",
          "meta-llama/Llama-2-7b-chat-hf",
          "meta-llama/Llama-3-70b-chat-hf",
          "meta-llama/Llama-3-8b-chat-hf",
          "meta-llama/Meta-Llama-3-70B-Instruct",
          "meta-llama/Meta-Llama-3-70B-Instruct-Lite",
          "meta-llama/Meta-Llama-3-70B-Instruct-Turbo",
          "meta-llama/Meta-Llama-3-8B-Instruct",
          "meta-llama/Meta-Llama-3-8B-Instruct-Lite",
          "meta-llama/Meta-Llama-3-8B-Instruct-Turbo",
          "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
          "meta-llama/Meta-Llama-3.1-70B-Instruct-Reference",
          "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
          "meta-llama/Meta-Llama-3.1-70B-Reference",
          "meta-llama/Meta-Llama-3.1-8B-Instruct-Reference",
          "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
          "microsoft/WizardLM-2-8x22B",
          "mistralai/Mistral-7B-Instruct-v0.1",
          "mistralai/Mistral-7B-Instruct-v0.2",
          "mistralai/Mistral-7B-Instruct-v0.3",
          "mistralai/Mixtral-8x22B-Instruct-v0.1",
          "mistralai/Mixtral-8x7B-Instruct-v0.1",
          "openchat/openchat-3.5-1210",
          "snorkelai/Snorkel-Mistral-PairRM-DPO",
          "teknium/OpenHermes-2-Mistral-7B",
          "teknium/OpenHermes-2p5-Mistral-7B",
          "togethercomputer/CodeLlama-13b-Instruct",
          "togethercomputer/CodeLlama-34b-Instruct",
          "togethercomputer/CodeLlama-7b-Instruct",
          "togethercomputer/Koala-13B",
          "togethercomputer/Koala-7B",
          "togethercomputer/Llama-2-7B-32K-Instruct",
          "togethercomputer/Llama-3-8b-chat-hf-int4",
          "togethercomputer/Llama-3-8b-chat-hf-int8",
          "togethercomputer/SOLAR-10.7B-Instruct-v1.0-int4",
          "togethercomputer/StripedHyena-Nous-7B",
          "togethercomputer/alpaca-7b",
          "togethercomputer/guanaco-13b",
          "togethercomputer/guanaco-33b",
          "togethercomputer/guanaco-65b",
          "togethercomputer/guanaco-7b",
          "togethercomputer/llama-2-13b-chat",
          "togethercomputer/llama-2-70b-chat",
          "togethercomputer/llama-2-7b-chat",
          "upstage/SOLAR-10.7B-Instruct-v1.0",
          "zero-one-ai/Yi-34B-Chat"
        ]
        fetch: false # fetching list of models is not supported
      titleConvo: true
      titleModel: "togethercomputer/llama-2-7b-chat"
      summarize: false
      summaryModel: "togethercomputer/llama-2-7b-chat"
      modelDisplayLabel: "together.ai"
```

The model list above was last updated on August 1, 2024.

## Notes

- Fetching the model list is not supported, so `fetch` is set to `false` and you maintain the `default` array yourself.
- together.ai's catalog changes often. Use the script below to pull the current chat models instead of editing the list by hand.

<Callout type="tip" title="Fetch and order the models" collapsible>
This Python script fetches the available chat models and writes them, sorted, to `models_togetherai.json` in a format that is easy to paste into the yaml config. Set your API key first.

```py filename="fetch_togetherai.py"
import json

import requests

# API key
api_key = ""

# API endpoint
url = "https://api.together.xyz/v1/models"

# headers
headers = {
    "accept": "application/json",
    "Authorization": f"Bearer {api_key}"
}

# make request
response = requests.get(url, headers=headers)

# parse JSON response
data = response.json()

# extract an ordered list of unique model IDs
model_ids = sorted(
    [
        model['id']
        for model in data
        if model['type'] == 'chat'
    ]
)

# write result to a text file
with open("models_togetherai.json", "w") as file:
    json.dump(model_ids, file, indent=2)
```
</Callout>


# TrueFoundry AI Gateway (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/truefoundry)

TrueFoundry AI Gateway is an enterprise proxy layer between your applications and LLM providers, giving access to 1000+ models through a unified OpenAI-compatible interface with built-in observability and governance.

## Get an API key

Authenticate with a Personal Access Token. Generate one by following the [PAT guide](https://docs.truefoundry.com/gateway/authentication#personal-access-token-pat), then add it along with your gateway URL to your `.env` file:

```bash filename=".env"
TRUEFOUNDRY_API_KEY=your-api-key
TRUEFOUNDRY_GATEWAY_URL=your-gateway-url
```

Get the gateway URL and model names from the unified code snippet in the TrueFoundry console. Use the same model names you added to the gateway. The [quick start guide](https://docs.truefoundry.com/gateway/quick-start) walks through setup.

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "TrueFoundry"
      apiKey: "${TRUEFOUNDRY_API_KEY}"
      baseURL: "${TRUEFOUNDRY_GATEWAY_URL}"
      models:
        default: ["openai-main/gpt-4o-mini", "openai-main/gpt-4o"]
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      summarize: false
      summaryModel: "current_model"
      modelDisplayLabel: "TrueFoundry:OpenAI"
```

## Notes

- Model names are namespaced by the integration you configured in the gateway, such as `openai-main/gpt-4o`. Match them to the names shown in your TrueFoundry console.
- For more detail, see the [TrueFoundry docs](https://docs.truefoundry.com/docs/introduction).


# vLLM (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/vllm)

vLLM is a high-throughput, memory-efficient inference and serving engine for LLMs. It exposes an OpenAI-compatible API, so you can run it locally and point LibreChat at your own server.

## Configuration

Local vLLM deployments don't require authentication, so the API key is just a placeholder. Point `baseURL` at your running vLLM server. Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "vLLM"
      apiKey: "vllm"
      baseURL: "http://127.0.0.1:8023/v1"
      models:
        default: ['google/gemma-3-27b-it']
        fetch: true
      titleConvo: true
      titleModel: "current_model"
      titleMessageRole: "user"
      summarize: false
      summaryModel: "current_model"
```

## Notes

- The example connects to a local vLLM server on port 8023 with Gemma 3 27B as the default. Set `baseURL` to wherever your server is running.
- With `fetch: true`, LibreChat loads the full list of models available on your vLLM server, so `default` is only the initial selection.
- `titleMessageRole: "user"` overrides the default `system` role for title generation. Some local models reject system message roles, so sending the title prompt as a user message avoids errors.


# Vultr Cloud Inference (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/vultrcloudinference)

Vultr Cloud Inference serves open models through an OpenAI-compatible API.

## Get an API key

Create a key from the [Vultr Cloud Inference](https://docs.vultr.com/vultr-cloud-inference) console. Add it to your `.env` file:

```bash filename=".env"
VULTRINFERENCE_TOKEN=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: 'Vultr Cloud Inference'
      apiKey: '${VULTRINFERENCE_TOKEN}'
      baseURL: 'https://api.vultrinference.com/v1/chat/completions'
      models:
        default: [
          "llama2-7b-chat-Q5_K_M.gguf",
          "llama2-13b-chat-Q5_K_M.gguf",
          "mistral-7b-Q5_K_M.gguf",
          "zephyr-7b-beta-Q5_K_M.gguf",
        ]
        fetch: true
      titleConvo: true
      titleModel: "llama2-7b-chat-Q5_K_M.gguf"
      modelDisplayLabel: "Vultr Cloud Inference"
```

## Notes

- The example lists four models optimized for chat, last updated June 28, 2024.
- Only `llama2-7b-chat-Q5_K_M.gguf` currently supports title generation.


# xAI (https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/xai)

xAI serves its Grok models through an OpenAI-compatible API that you can add to LibreChat as a custom endpoint.

## Get an API key

Create a key from the [xAI console](https://console.x.ai/). Add it to your `.env` file:

```bash filename=".env"
XAI_API_KEY=your-api-key
```

## Configuration

Add the endpoint under `endpoints.custom` in your `librechat.yaml`:

```yaml filename="librechat.yaml"
    - name: "xai"
      apiKey: "${XAI_API_KEY}"
      baseURL: "https://api.x.ai/v1"
      models:
        default: ["grok-beta"]
        fetch: false
      titleConvo: true
      titleMethod: "completion"
      titleModel: "grok-beta"
      summarize: false
      summaryModel: "grok-beta"
      modelDisplayLabel: "Grok"
```

## Notes

- `titleMethod: "completion"` generates conversation titles through the chat completions endpoint, which is what Grok supports.


# Config Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/config)

**Note:** Fields not specifically mentioned as required are optional.

## version

- **required**

<OptionTable
  options={[
    ['version', 'String', 'Specifies the version of the configuration file.', 'version: 1.3.15'],
  ]}
/>

## cache

<OptionTable
  options={[
    [
      'cache',
      'Boolean',
      'Toggles caching on or off. Set to `true` to enable caching (default).',
      'cache: true',
    ],
  ]}
/>

## langfuse

<OptionTable
  options={[
    [
      'langfuse',
      'Object',
      'Configures the encrypted Langfuse connection and deployment-owned request headers.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['enabled', 'Boolean', 'Enables or disables Langfuse tracing for this config scope.', ''],
    ['publicKey', 'String', 'Langfuse project public key.', ''],
    ['secretKey', 'String', 'Encrypted Langfuse project secret key.', ''],
    ['projectId', 'String', 'Verified Langfuse project identity.', ''],
    ['secretKeyPreview', 'String', 'Server-generated masked preview of the stored secret key.', ''],
    [
      'destination',
      'String',
      'Selects one deployment-approved Langfuse destination key.',
      '',
    ],
    [
      'headers',
      'Object/Map of Strings',
      'Deployment-owned headers sent to one configured Langfuse origin for proxy or gateway authentication. Supports `${ENV_VAR}` references.',
      '',
    ],
  ]}
/>

Manage connection fields through **Settings → Langfuse** or an authorized administrator configuration client. The Settings flow verifies the connection and derives `projectId`; authorized administrator writes encrypt `secretKey`, generate `secretKeyPreview`, and redact the secret from reads. Plaintext `secretKey` values placed directly in `librechat.yaml` are not accepted by the runtime connection path. The legacy `displaySecretKey` and `fanout.enabled` fields are no longer part of the schema.

`headers` is different: it is deployment infrastructure and can only be set in `librechat.yaml`. Admin configuration writes reject both the whole map and individual header paths so gateway credentials are not stored or returned through Mongo-backed configuration. Values support `${ENV_VAR}` interpolation; use environment references instead of literals. LibreChat drops unresolved variables, protected infrastructure-secret references, blank values, and invalid HTTP header names with a warning.

Custom headers are sent on trace and media export, feedback-score requests, project lookup, and admin credential verification only when the deployment resolves exactly one Langfuse origin. If central, tenant, or collector configuration produces multiple origins, LibreChat sends no custom headers and logs a warning because the map cannot safely identify a recipient. They cannot use per-user `{{...}}` placeholders, and the fanout collector forwards only `Authorization` upstream.

```yaml filename="langfuse / headers"
langfuse:
  headers:
    CF-Access-Client-Id: '${CF_ACCESS_CLIENT_ID}'
    CF-Access-Client-Secret: '${CF_ACCESS_CLIENT_SECRET}'
```

`langfuse` is a base-configuration-only section. Role, group, and user configuration overrides cannot replace or tombstone it.

See [Langfuse Configuration](/docs/configuration/langfuse) for availability rules, authenticated proxy setup, environment-managed credentials, and optional fanout deployment.

## skillSync

<OptionTable
  options={[
    [
      'skillSync',
      'Object',
      'Configures external Skill mirroring. In v1.3.13, GitHub Skill Sync is supported.',
      '',
    ],
  ]}
/>

see: [Skill Sync Object Structure](/docs/configuration/librechat_yaml/object_structure/skill_sync)

## filters

<OptionTable
  options={[
    [
      'filters',
      'Object',
      'Configures source-aware content protection for messages, prompts, Agent instructions, conversation starters and titles, feedback, Skills, memories, files, tool arguments, model parameters, and Action metadata.',
      '',
    ],
  ]}
/>

`filters` is a base-configuration-only policy. Role, group, user, and database overrides cannot add, replace, or tombstone it. In a multi-replica deployment, coordinate the config rollout or restart so every replica loads the same policy.

See: [Content Filter Object Structure](/docs/configuration/librechat_yaml/object_structure/message_filter#source-aware-filters)

## messageFilter

<OptionTable
  options={[
    [
      'messageFilter',
      'Object',
      'Configures the legacy message-only PII policy. Existing deployments can keep this block while migrating to `filters.messages`; when both are configured, both policies apply.',
      '',
    ],
  ]}
/>

See: [Legacy messageFilter](/docs/configuration/librechat_yaml/object_structure/message_filter#legacy-messagefilter)

## fileStrategy

- **Options**: "local" | "firebase" | "s3" | "azure_blob" | "cloudfront"

<OptionTable
  options={[
    [
      'fileStrategy',
      'String',
      'Determines where to save user uploaded/generated files. Defaults to `"local"` if omitted.',
      'fileStrategy: "firebase"',
    ],
  ]}
/>

- **Notes**:
  - `"cloudfront"` stores files in S3 and returns CloudFront URLs for stable media delivery, signed cookies, and signed downloads.
  - `"firebase"` serves files through Firebase Storage and Firebase Hosting edge locations.
  - S3 serves files via **presigned URLs** (temporary signed tokens) that expire. Once expired, any image or avatar referencing that URL will appear broken in the UI. This makes S3 unsuitable as a primary strategy for visual assets. See the [related discussion](https://github.com/danny-avila/LibreChat/discussions/10280#discussioncomment-14803903) for details.
  - For best performance of images and avatars, use `"cloudfront"` or `"firebase"`, or configure `fileStrategies` to route `avatar` and `image` to a CDN-backed strategy.
  - Please refer to the [File Storage & CDN documentation](/docs/configuration/cdn) for setup details

## fileStrategies

Allows granular control over file storage strategies for different file types.

- **Available Strategies**: "local" | "firebase" | "s3" | "azure_blob" | "cloudfront"

<OptionTable
  options={[
    [
      'fileStrategies',
      'Object',
      'Configures different storage strategies for different file types. More flexible than the single fileStrategy option.',
      '',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    [
      'default',
      'String',
      'Fallback storage strategy when specific type is not defined. Defaults to "local".',
      '',
    ],
    [
      'avatar',
      'String',
      'Storage strategy for user and agent avatar images. Recommended to use a CDN-backed strategy (`"cloudfront"` or `"firebase"`) for best performance.',
      '',
    ],
    [
      'image',
      'String',
      'Storage strategy for images uploaded in chats. Recommended to use a CDN-backed strategy (`"cloudfront"` or `"firebase"`) for best performance.',
      '',
    ],
    ['document', 'String', 'Storage strategy for document uploads (PDFs, text files, etc.).', ''],
    ['skills', 'String', 'Storage strategy for files bundled with Skills.', ''],
  ]}
/>

- **Notes**:
  - This setting takes precedence over the single `fileStrategy` option
  - If a specific file type is not configured, it falls back to `default`, then to `fileStrategy`, and finally to `"local"`
  - Images and avatars need persistent, stable URLs to render correctly across the UI. S3 presigned URLs expire (AWS cap: 7 days for IAM users, hours for STS/role-based credentials), causing broken images in the model selector and chat UI. See the [related discussion](https://github.com/danny-avila/LibreChat/discussions/10280#discussioncomment-14803903) for full context. Use `"cloudfront"` or `"firebase"` for `avatar` and `image` to avoid this.
  - S3 and Azure Blob Storage are well-suited for `document` storage, where short-lived presigned download URLs are appropriate.
  - Please refer to the [File Storage & CDN documentation](/docs/configuration/cdn) for setup details for each storage provider

**Examples:**

```yaml filename="fileStrategies - All in one place"
# Use a single strategy for all file types
fileStrategies:
  default: 's3'
```

```yaml filename="fileStrategies - Mixed strategies"
# Route images and avatars to CDN, keep documents in object storage
fileStrategies:
  avatar: 'cloudfront' # CDN delivery for avatars
  image: 'cloudfront' # CDN delivery for generated/uploaded images
  document: 's3' # Object storage for documents
```

```yaml filename="fileStrategies - Partial configuration"
# Only configure specific types, others use default
fileStrategies:
  default: 'local'
  avatar: 'firebase' # Only avatars use Firebase CDN, everything else is local
```

## cloudfront

**Key:**

<OptionTable
  options={[['cloudfront', 'Object', 'Configures CloudFront delivery for files stored in S3.', '']]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'domain',
      'String',
      'CloudFront distribution domain or CNAME. Required when any file strategy uses `"cloudfront"`.',
      'domain: "https://cdn.example.com"',
    ],
    [
      'distributionId',
      'String',
      'CloudFront distribution ID. Required when `invalidateOnDelete` is true.',
      'distributionId: "E1234ABCD"',
    ],
    [
      'invalidateOnDelete',
      'Boolean',
      'Creates a CloudFront invalidation for deleted files. Default: false.',
      'invalidateOnDelete: false',
    ],
    [
      'imageSigning',
      'String',
      'Controls inline image/avatar access. Options: `"none"` or `"cookies"`. `"url"` is reserved and not implemented for images.',
      'imageSigning: "cookies"',
    ],
    [
      'cookieDomain',
      'String',
      'Shared parent cookie domain required for signed cookies. Must start with a dot.',
      'cookieDomain: ".example.com"',
    ],
    [
      'cookieExpiry',
      'Number',
      'Signed cookie lifetime in seconds. Default: 1800, maximum: 604800.',
      'cookieExpiry: 1800',
    ],
    [
      'urlExpiry',
      'Number',
      'Signed CloudFront download URL lifetime in seconds. Default: 3600.',
      'urlExpiry: 3600',
    ],
    [
      'storageRegion',
      'String',
      'Optional region label used in generated object keys when region paths are enabled.',
      'storageRegion: "us-east-2"',
    ],
    [
      'includeRegionInPath',
      'Boolean',
      'Includes the storage region in newly generated object keys. Default: false.',
      'includeRegionInPath: false',
    ],
    [
      'requireSignedAccess',
      'Boolean',
      'Refuses startup when signed-cookie CloudFront access cannot initialize. Default: false.',
      'requireSignedAccess: true',
    ],
  ]}
/>

see: [CloudFront Object Structure](/docs/configuration/librechat_yaml/object_structure/cloudfront) and [CloudFront with S3](/docs/configuration/cdn/cloudfront)

## filteredTools

<OptionTable
  options={[
    [
      'filteredTools',
      'Array of Strings',
      'Filters out specific tools from both Plugins and OpenAI Assistants endpoints.',
      'filteredTools: ["scholarai", "calculator"]',
    ],
  ]}
/>

- **Notes**:
  - If `includedTools` and `filteredTools` are both specified, only `includedTools` will be recognized.
  - Affects both `gptPlugins` and `assistants` endpoints
  - You can find the names of the tools to filter in [`api/app/clients/tools/manifest.json`](https://github.com/danny-avila/LibreChat/blob/main/api/app/clients/tools/manifest.json)
    - Use the `pluginKey` value
  - Also, any listed under the ".well-known" directory `api/app/clients/tools/.well-known`
    - Use the `name_for_model` value

## includedTools

<OptionTable
  options={[
    [
      'includedTools',
      'Array of Strings',
      'Includes specific tools from both Plugins and OpenAI Assistants endpoints.',
      'includedTools: ["calculator"]',
    ],
  ]}
/>

- **Notes**:
  - If `includedTools` and `filteredTools` are both specified, only `includedTools` will be recognized.
  - Affects both `gptPlugins` and `assistants` endpoints
  - You can find the names of the tools to filter in [`api/app/clients/tools/manifest.json`](https://github.com/danny-avila/LibreChat/blob/main/api/app/clients/tools/manifest.json)
    - Use the `pluginKey` value
  - Also, any listed under the ".well-known" directory `api/app/clients/tools/.well-known`
    - Use the `name_for_model` value

## secureImageLinks

<OptionTable
  options={[
    [
      'secureImageLinks',
      'Boolean',
      'Requires authorization for image links hosted locally by the app. Default: true.',
      'secureImageLinks: true',
    ],
  ]}
/>

Local images are protected when this field is omitted. Private conversation images require an active session and owner access. Stored user avatars require an authenticated viewer in the same tenant. Agent avatars follow the Agent's view ACL, including public visibility, while Assistant avatars require the same tenant plus the effective endpoint sharing or Assistant-management policy. Authorization and configuration lookup failures fail closed.

Set `secureImageLinks: false` only as a compatibility opt-out for deployments that intentionally expose local image URLs without authentication. Role and user configuration overrides are resolved from the image owner's effective configuration.

## imageOutputType

- **Note**: Case-sensitive. Google endpoint only supports "jpeg" and "png" output types.
- **Options**: "png" | "webp" | "jpeg"

<OptionTable
  options={[
    [
      'imageOutputType',
      'String',
      'The image output type for image responses. Defaults to "png" if omitted.',
      'imageOutputType: "webp"',
    ],
  ]}
/>

## ocr

**Key:**

<OptionTable
  options={[
    [
      'ocr',
      'Object',
      'Configures Optical Character Recognition (OCR) settings for extracting text from images.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['apiKey', 'String', 'The API key for the OCR service.', ''],
    ['baseURL', 'String', 'The base URL for the OCR service API.', ''],
    [
      'strategy',
      'String',
      'The OCR strategy to use. Options are "mistral_ocr", "azure_mistral_ocr", "vertexai_mistral_ocr", "document_parser", or "custom_ocr".',
      '',
    ],
    ['mistralModel', 'String', 'The Mistral model to use for OCR processing.', ''],
    [
      'allowedAddresses',
      'Array of Strings',
      'Trusted private host:port exemptions for OCR connect-time SSRF checks. Public destinations remain available.',
      '',
    ],
  ]}
/>

see: [OCR Config Object Structure](/docs/configuration/librechat_yaml/object_structure/ocr)

## webSearch

**Key:**

<OptionTable
  options={[
    [
      'webSearch',
      'Object',
      'Configures web search functionality, including search providers, content scrapers, and result rerankers.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'serperApiKey',
      'String',
      'Environment variable name for the Serper API key. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'searxngInstanceUrl',
      'String',
      'Environment variable name for the SearXNG instance URL. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'searxngApiKey',
      'String',
      'Environment variable name for the SearXNG API key. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'tavilyApiKey',
      'String',
      'Environment variable name for the Tavily API key. Used for both search and scraper. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'tavilySearchUrl',
      'String',
      'Environment variable name for a custom Tavily Search API URL. Optional; defaults to Tavily hosted search when unset.',
      '',
    ],
    [
      'tavilyExtractUrl',
      'String',
      'Environment variable name for a custom Tavily Extract API URL. Optional; defaults to Tavily hosted extract when unset.',
      '',
    ],
    [
      'firecrawlApiKey',
      'String',
      'Environment variable name for the Firecrawl API key. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'firecrawlApiUrl',
      'String',
      'Environment variable name for the Firecrawl API URL. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'jinaApiKey',
      'String',
      'Environment variable name for the Jina API key. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'cohereApiKey',
      'String',
      'Environment variable name for the Cohere API key. If not set in .env, users will be prompted to provide it via UI.',
      '',
    ],
    [
      'searchProvider',
      'String',
      'Specifies which search provider to use. Options: "serper", "searxng", "tavily".',
      '',
    ],
    [
      'scraperProvider',
      'String',
      'Specifies which scraper service to use. Options: "firecrawl", "serper", "tavily".',
      '',
    ],
    ['firecrawlVersion', 'String', 'Specifies Firecrawl API version (v0 or v1).', ''],
    [
      'rerankerType',
      'String',
      'Specifies which reranker service to use. Set to "none" to skip reranking. Options: "jina", "cohere", "none".',
      '',
    ],
    [
      'scraperTimeout',
      'Integer',
      'Timeout in milliseconds for scraper requests. Must be a non-negative integer.',
      '',
    ],
    [
      'safeSearch',
      'Number',
      'Safe search filtering level. 0 = OFF, 1 = MODERATE (default), 2 = STRICT.',
      '',
    ],
    [
      'allowedAddresses',
      'Array of Strings',
      'Trusted private host:port exemptions for web search, scrape, and rerank connect-time SSRF checks. Public destinations remain available.',
      '',
    ],
  ]}
/>

see: [Web Search Object Structure](/docs/configuration/librechat_yaml/object_structure/web_search)

## fileConfig

**Key:**

<OptionTable
  options={[
    [
      'fileConfig',
      'Object',
      'Configures file handling settings for the application, including size limits and MIME type restrictions.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'endpoints',
      'Record/Object',
      'Specifies file handling configurations for individual endpoints, allowing customization per endpoint basis.',
      '',
    ],
    [
      'serverFileSizeLimit',
      'Number',
      'The maximum file size (in MB) that the server will accept. Applies globally across all endpoints unless overridden by endpoint-specific settings.',
      '',
    ],
    ['avatarSizeLimit', 'Number', 'Maximum size (in MB) for user avatar images.', ''],
    [
      'clientImageResize',
      'Object',
      'Configures client-side image resizing to optimize file uploads and prevent upload errors due to large image sizes.',
      '',
    ],
    ['ocr', 'Object', 'Settings for Optical Character Recognition (OCR) file processing.', ''],
    ['text', 'Object', 'Settings for direct text file parsing.', ''],
    ['stt', 'Object', 'Settings for Speech-to-Text (STT) audio file processing.', ''],
    [
      'fileTokenLimit',
      'Number',
      'Maximum number of tokens from text files to include in prompts before truncation.',
      'fileTokenLimit: 100000',
    ],
  ]}
/>

## clientImageResize

**Key:**

<OptionTable
  options={[
    [
      'clientImageResize',
      'Object',
      'Configures client-side image resizing to optimize file uploads and prevent upload errors due to large image sizes.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'When explicitly set, forces client-side resizing on or off for every user and locks the Settings toggle. Omit it to let each user choose in Settings > Chat; the user preference defaults to off.',
      'enabled: true',
    ],
    [
      'maxWidth',
      'Number',
      'Maximum width in pixels for resized images. Must be at least 1. Default: 1900.',
      'maxWidth: 1900',
    ],
    [
      'maxHeight',
      'Number',
      'Maximum height in pixels for resized images. Must be at least 1. Default: 1900.',
      'maxHeight: 1900',
    ],
    [
      'quality',
      'Number',
      'Browser encoder quality from 0 to 1. Higher values usually preserve more detail and produce larger files. Default: 0.92.',
      'quality: 0.92',
    ],
  ]}
/>

**Description:**

The `clientImageResize` configuration controls client-side downscaling before upload. This feature helps:

- **Prevent upload failures** due to large image files exceeding server limits
- **Reduce bandwidth usage** by compressing images before transmission
- **Improve upload performance** with smaller file sizes
- **Maintain image quality** while optimizing file size

When resizing is enabled, supported images that exceed `maxWidth` or `maxHeight` are downscaled in the browser before upload. LibreChat preserves the aspect ratio, never upscales smaller images, and keeps the original file when the encoded result would not be smaller.

If `enabled` is omitted, users can turn **Resize images before upload** on or off under **Settings > Chat**. The preference is stored in that browser and defaults to off. Setting `enabled: true` or `enabled: false` in `librechat.yaml` overrides every user's preference and disables the toggle.

**Example:**

```yaml filename="clientImageResize"
fileConfig:
  clientImageResize:
    # Omit enabled so each user can choose under Settings > Chat.
    maxWidth: 1900
    maxHeight: 1900
    quality: 0.92
```

To enforce one behavior for the deployment, add either `enabled: true` or `enabled: false` to the same block.

**Notes:**

- The resize pipeline supports JPEG, PNG, and WebP in browsers with the required Canvas APIs.
- Animated PNG and WebP files are sent unchanged so resizing does not discard animation.
- The output keeps the source format. There is no `compressFormat` setting.
- Browser encoders may ignore `quality` for lossless formats such as PNG.
- A resize failure falls back to the original file; normal server upload limits still apply.

see: [File Config Object Structure](/docs/configuration/librechat_yaml/object_structure/file_config)

## rateLimits

**Key:**

<OptionTable
  options={[
    [
      'rateLimits',
      'Object',
      'Defines rate limiting policies to prevent abuse by limiting the number of requests.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'fileUploads',
      'Object',
      'Configures rate limits specifically for file upload operations.',
      '',
    ],
    [
      'conversationsImport',
      'Object',
      'Configures rate limits specifically for conversation import operations.',
      '',
    ],
    [
      'agentEvents',
      'Object',
      'Configures the API-key-principal admission limit for authenticated Agent event requests.',
      '',
    ],
    ['stt', 'Object', 'Configures rate limits specifically for speech-to-text (stt) requests', ''],
    ['tts', 'Object', 'Configures rate limits specifically for text-to-speech (tts) requests', ''],
  ]}
/>

**fileUploads Subkeys:**

<OptionTable
  options={[
    ['ipMax', 'Number', 'Maximum number of uploads allowed per IP address per window.', ''],
    ['ipWindowInMinutes', 'Number', 'Time window in minutes for the IP-based upload limit.', ''],
    ['userMax', 'Number', 'Maximum number of uploads allowed per user per window.', ''],
    [
      'userWindowInMinutes',
      'Number',
      'Time window in minutes for the user-based upload limit.',
      '',
    ],
  ]}
/>

**conversationsImport Subkeys:**

<OptionTable
  options={[
    ['ipMax', 'Number', 'Maximum number of imports allowed per IP address per window.', ''],
    ['ipWindowInMinutes', 'Number', 'Time window in minutes for the IP-based imports limit.', ''],
    ['userMax', 'Number', 'Maximum number of imports per user per window.', ''],
    [
      'userWindowInMinutes',
      'Number',
      'Time window in minutes for the user-based imports limit.',
      '',
    ],
  ]}
/>

**agentEvents Subkeys:**

<OptionTable
  options={[
    [
      'userMax',
      'Number',
      'Maximum authenticated Agent event admissions per Remote Agents API key principal in one window.',
      '40',
    ],
    [
      'userWindowInMinutes',
      'Number',
      'Length of the authenticated Agent event admission window in minutes.',
      '1',
    ],
  ]}
/>

This admission bucket is separate from normal message execution limits. The durable worker consumes the normal message-user bucket when it executes a delivery, avoiding a double charge at admission time. Legacy `AGENT_EVENT_USER_MAX` and `AGENT_EVENT_USER_WINDOW` values remain fallbacks when the YAML fields are omitted; explicit YAML values take precedence.

**tts Subkeys:**

<OptionTable
  options={[
    ['ipMax', 'Number', 'Maximum number of requests allowed per IP address per window.', ''],
    ['ipWindowInMinutes', 'Number', 'Time window in minutes for the IP-based requests limit.', ''],
    ['userMax', 'Number', 'Maximum number of requests per user per window.', ''],
    [
      'userWindowInMinutes',
      'Number',
      'Time window in minutes for the user-based requests limit.',
      '',
    ],
  ]}
/>

**stt Subkeys:**

<OptionTable
  options={[
    ['ipMax', 'Number', 'Maximum number of requests allowed per IP address per window.', ''],
    ['ipWindowInMinutes', 'Number', 'Time window in minutes for the IP-based requests limit.', ''],
    ['userMax', 'Number', 'Maximum number of requests per user per window.', ''],
    [
      'userWindowInMinutes',
      'Number',
      'Time window in minutes for the user-based requests limit.',
      '',
    ],
  ]}
/>

    - **Example**:
    ```yaml filename="rateLimits"
    rateLimits:
      agentEvents:
        userMax: 40
        userWindowInMinutes: 1
      fileUploads:
        ipMax: 100
        ipWindowInMinutes: 60
        userMax: 50
        userWindowInMinutes: 60
      conversationsImport:
        ipMax: 100
        ipWindowInMinutes: 60
        userMax: 50
        userWindowInMinutes: 60
      stt:
        ipMax: 100
        ipWindowInMinutes: 1
        userMax: 50
        userWindowInMinutes: 1
      tts:
        ipMax: 100
        ipWindowInMinutes: 1
        userMax: 50
        userWindowInMinutes: 1
    ```

## registration

**Key:**

<OptionTable
  options={[
    ['registration', 'Object', 'Configures registration-related settings for the application.', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['socialLogins', '', 'Social login configurations.', ''],
    ['allowedDomains', '', 'Specifies allowed domains for registration.', ''],
  ]}
/>

see also:

- [socialLogins](/docs/configuration/librechat_yaml/object_structure/registration#sociallogins),
- [alloweddomains](/docs/configuration/librechat_yaml/object_structure/registration#alloweddomains),
- [Registration Object Structure](/docs/configuration/librechat_yaml/object_structure/registration)

## memory

**Key:**

<OptionTable
  options={[
    [
      'memory',
      'Object',
      'Configures conversation memory and personalization features for the application.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['disabled', 'Boolean', 'Disables memory functionality when set to true.', ''],
    ['validKeys', 'Array of Strings', 'Specifies which keys are valid for memory storage.', ''],
    [
      'tokenLimit',
      'Number',
      'Sets the maximum number of tokens for memory storage and processing.',
      '',
    ],
    [
      'charLimit',
      'Number',
      'Sets the maximum number of characters for memory storage. Default: 10000.',
      '',
    ],
    [
      'maxInputTokens',
      'Number',
      'Caps the recent-chat tokens sent to the automatic memory agent before extraction. Default: 12000.',
      '',
    ],
    ['personalize', 'Boolean', 'Enables or disables personalization features.', ''],
    [
      'messageWindowSize',
      'Number',
      'Specifies the number of recent messages to include in memory context.',
      '',
    ],
    [
      'agent',
      'Object | Union',
      'Configures the optional automatic memory agent. Set `agent.enabled: true` to run it.',
      '',
    ],
  ]}
/>

see: [Memory Object Structure](/docs/configuration/librechat_yaml/object_structure/memory)

## summarization

**Key:**

<OptionTable
  options={[
    [
      'summarization',
      'Object',
      'Configures conversation summarization and context pruning. Replaces the per-endpoint `summarize` and `summaryModel` fields.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'provider',
      'String',
      "LLM provider for summarization calls. Defaults to the agent's own provider.",
      '',
    ],
    ['model', 'String', "Model for summarization calls. Defaults to the agent's own model.", ''],
    ['parameters', 'Object', 'Additional LLM parameters for summarization requests.', ''],
    ['prompt', 'String', 'Custom prompt for initial summarization.', ''],
    ['updatePrompt', 'String', 'Custom prompt for re-compaction when a prior summary exists.', ''],
    [
      'trigger',
      'Object',
      'Defines when summarization is triggered (by token ratio, remaining tokens, or message count).',
      '',
    ],
    [
      'maxSummaryTokens',
      'Number',
      'Maximum output tokens for the summarization model response.',
      '',
    ],
    [
      'reserveRatio',
      'Number',
      'Fraction of token budget reserved as headroom (0–1). Default: 0.05.',
      '',
    ],
    [
      'contextPruning',
      'Object',
      'Configures position-based tool result degradation for older messages.',
      '',
    ],
    [
      'retainRecent',
      'Object',
      'Preserves recent complete turns and/or tokens outside the generated summary.',
      '',
    ],
  ]}
/>

see: [Summarization Object Structure](/docs/configuration/librechat_yaml/object_structure/summarization)

## actions

**Key:**

<OptionTable
  options={[
    ['actions', 'Object', 'Configures actions-related settings, used by Agents and Assistants', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'allowedDomains',
      'Array of Strings',
      'Strict whitelist of domains for actions. When set, only listed domains are reachable.',
      '',
    ],
    [
      'allowedAddresses',
      'Array of Strings',
      'SSRF exemption list (private IP space only). Permits specific private host:port services without restricting public destinations when `allowedDomains` is not configured.',
      '',
    ],
  ]}
/>

see also:

- [allowedDomains](/docs/configuration/librechat_yaml/object_structure/actions#alloweddomains),
- [allowedAddresses](/docs/configuration/librechat_yaml/object_structure/actions#allowedaddresses),
- [Actions Object Structure](/docs/configuration/librechat_yaml/object_structure/actions)

## interface

**Key:**

<OptionTable
  options={[
    [
      'interface',
      'Object',
      'Configures user interface elements within the application, allowing for customization of visibility and behavior of various components.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'privacyPolicy',
      'Object',
      'Contains settings related to the privacy policy link provided.',
      '',
    ],
    [
      'termsOfService',
      'Object',
      'Contains settings related to the terms of service link provided.',
      '',
    ],
    ['modelSelect', 'Boolean', 'Determines whether the model selection feature is available.', ''],
    [
      'parameters',
      'Boolean',
      'Toggles the visibility of parameter configuration options AKA conversation settings.',
      '',
    ],
    ['presets', 'Boolean', 'Enables or disables the presets menu', ''],
    [
      'prompts',
      'Boolean or Object',
      'Enables or disables all prompt-related features for all users',
      '',
    ],
    [
      'bookmarks',
      'Boolean',
      'Enables or disables all bookmarks-related features for all users',
      '',
    ],
    ['memories', 'Boolean', 'Enables or disables the memories feature for all users', ''],
    [
      'multiConvo',
      'Boolean',
      'Enables or disables all "multi convo", AKA multiple response streaming, related features for all users',
      '',
    ],
    ['agents', 'Boolean or Object', 'Enables or disables all agents features for all users', ''],
    ['temporaryChat', 'Boolean', 'Enables or disables the temporary chat feature', ''],
    [
      'temporaryChatRetention',
      'Number',
      'Configures the retention period for temporary chats in hours. Min: 1, Max: 8760. Default: 720 (30 days).',
      '',
    ],
    [
      'autoSubmitFromUrl',
      'Boolean',
      'Controls whether `/c/new?prompt=…&submit=true` auto-submits to the model. When `false`, the prompt is pre-filled but not submitted.',
      '',
    ],
    [
      'mcpServers',
      'Object',
      'Contains settings related to MCP server selection and access control.',
      '',
    ],
    ['customWelcome', 'String', 'Custom welcome message displayed in the chat interface.', ''],
    [
      'runCode',
      'Boolean',
      'Enables or disables the "Run Code" button for Markdown Code Blocks',
      '',
    ],
    ['webSearch', 'Boolean', 'Enables or disables the web search button in the chat interface', ''],
    [
      'fileSearch',
      'Boolean',
      'Enables or disables the file search button in the chat interface',
      '',
    ],
    ['fileCitations', 'Boolean', 'Globally enables or disables file citations for all users', ''],
    [
      'feedback',
      'Boolean',
      'Shows or hides the thumbs up/thumbs down feedback buttons on responses',
      '',
    ],
    [
      'peoplePicker',
      'Object',
      'Configures which principal types are available controls in the people picker interface',
      '',
    ],
    ['marketplace', 'Object', 'Enables or disables access to the Agent Marketplace', ''],
  ]}
/>
see: [Interface Object Structure](/docs/configuration/librechat_yaml/object_structure/interface)

## modelSpecs

**Key:**

<OptionTable
  options={[
    [
      'modelSpecs',
      'Object',
      'Configures model specifications, allowing for detailed setup and customization of AI models and their behaviors within the application.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'enforce',
      'Boolean',
      'Determines whether the model specifications should strictly override other configuration settings.',
      '',
    ],
    [
      'prioritize',
      'Boolean',
      'Specifies if model specifications should take priority over the default configuration when both are applicable.',
      '',
    ],
    [
      'list',
      'Array of Objects',
      'Contains a list of individual model specifications detailing various configurations and behaviors.',
      '',
    ],
  ]}
/>

see: [Model Specs Object Structure](/docs/configuration/librechat_yaml/object_structure/model_specs)

## endpoints

**Key:**

<OptionTable
  options={[['endpoints', 'Object', 'Defines custom API endpoints for the application.', '']]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'custom',
      'Array of Objects',
      'Each object in the array represents a unique endpoint configuration.',
      '',
    ],
    ['azureOpenAI', 'Object', 'Azure OpenAI endpoint-specific configuration', ''],
    ['assistants', 'Object', 'Assistants endpoint-specific configuration.', ''],
    ['azureAssistants', 'Object', 'Azure Assistants endpoint-specific configuration.', ''],
    ['agents', 'Object', 'Agents endpoint-specific configuration.', ''],
    [
      'all',
      'Object',
      'Global endpoint settings that apply to all endpoints. See Shared Endpoint Settings.',
      '',
    ],
    [
      'allowedAddresses',
      'Array of Strings',
      'SSRF exemption list (private IP space only). Permits user-provided baseURLs to point at specific private host:port services (e.g. self-hosted Ollama) without disabling SSRF protection for everything else.',
      '',
    ],
  ]}
/>

> **Note:** Endpoints support [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings) such as `streamRate`, `headers`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, `titleEndpoint`, and `maxToolResultChars`. These can be configured individually per endpoint or globally using the `all` key. `headers` are merged with endpoint-level values winning on key collisions. The `all` key does not accept `baseURL`.

> **Note:** `endpoints.allowedAddresses` applies to user-provided `baseURL` values (when an admin configures a custom endpoint with `apiKey: 'user_provided'` and `baseURL: 'user_provided'`). Each user-supplied baseURL is validated against the SSRF block at request time; entries listed here are exempted. See [`mcpSettings.allowedAddresses`](/docs/configuration/librechat_yaml/object_structure/mcp_settings#allowedaddresses) for the field semantics — same rules apply (private IP space only, port required, no URLs/paths/CIDR/bare hosts/public IP literals).

## mcpSettings

**Key:**

<OptionTable
  options={[
    [
      'mcpSettings',
      'Object',
      'Defines global settings for Model Context Protocol (MCP) servers',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'allowedDomains',
      'Array of Strings',
      'Strict whitelist of domains for MCP server connections. When set, only listed entries are reachable.',
      '',
    ],
    [
      'allowedAddresses',
      'Array of Strings',
      'SSRF exemption list (private IP space only). Permits specific private host:port services without flipping `allowedDomains` into strict-whitelist mode.',
      '',
    ],
  ]}
/>

- **Notes**:
  - This is a security feature to protect against abuse / misuse of internal addresses via MCP servers
  - By default, LibreChat restricts MCP servers from connecting to internal, local, or private network addresses
  - MCP servers using local IP addresses or domains can either be added to the strict `allowedDomains` whitelist (which then becomes the only reachable set), or — to keep public destinations reachable — exempted as exact host:port services via `allowedAddresses`
  - As with all yaml configuration changes, a LibreChat restart is required to take effect
  - Supports domains, wildcard subdomains (`*.example.com`), docker domains, and IP addresses

**Example:**

```yaml filename="mcpSettings"
mcpSettings:
  # Strict whitelist mode:
  # allowedDomains:
  #   - "example.com"           # Specific domain
  #   - "*.example.com"         # All subdomains
  #   - "http://mcp-server:3000" # Internal service, explicitly whitelisted

  # Default SSRF mode with private service exemptions:
  allowedAddresses:
    - 'host.docker.internal:8080' # Permit one private host on one port
    - '10.0.0.5:8000' # Permit one private IP on one port
```

see: [MCP Settings Object Structure](/docs/configuration/librechat_yaml/object_structure/mcp_settings)

## mcpServers

**Key:**

<OptionTable
  options={[
    [
      'mcpServers',
      'Object',
      'Defines the configuration for Model Context Protocol (MCP) servers, allowing dynamic integration of MCP servers within the application.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      '<serverName>',
      'Object',
      'Each key under `mcpServers` represents an individual MCP server configuration, identified by a unique name.',
      '',
    ],
  ]}
/>

- **Notes**:
  - Initialization happens at startup, and the app must be restarted for changes to take effect.
  - The `<serverName>` is a unique identifier for each MCP server configuration.
  - Each MCP server can be configured using one of four connection types:
    - `stdio`
    - `websocket`
    - `sse`
    - `streamable-http`
  - The `type` field specifies the connection type to the MCP server.
  - If `type` is omitted, it defaults based on the presence and format of `url` or `command`:
    - If `url` is specified and starts with `http` or `https`, `type` defaults to `sse`.
    - If `url` is specified and starts with `ws` or `wss`, `type` defaults to `websocket`.
    - If `command` is specified, `type` defaults to `stdio`.
  - Additional configuration options include:
    - `timeout`: Timeout in milliseconds for MCP server requests. Determines how long to wait for a response for tool requests.
    - `initTimeout`: Timeout in milliseconds for MCP server initialization. Determines how long to wait for the server to initialize.
    - `serverInstructions`: Controls whether server instructions are included in agent context. Can be `true` (use server-provided), `false` (disabled), or a custom string (overrides server-provided).
    - `customUserVars`: (Optional) Defines custom variables (e.g., API keys, URLs) that individual users can set for an MCP server. These per-user values, provided through the UI, can then be referenced in the server's `headers` or `env` configurations using `{{VARIABLE_NAME}}` syntax. This allows for per-user authentication or customization for MCP tools.
  - see: [MCP Servers Object Structure](/docs/configuration/librechat_yaml/object_structure/mcp_servers)

**Example:**

```yaml filename="mcpServers"
mcpServers:
  everything:
    # type: sse # type can optionally be omitted
    url: http://localhost:3001/sse
    timeout: 30000
    initTimeout: 10000
    serverInstructions: true # Use server-provided instructions
  puppeteer:
    type: stdio
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-puppeteer'
    timeout: 30000
    initTimeout: 10000
    serverInstructions: 'Do not access any local files or local/internal IP addresses'
  filesystem:
    # type: stdio
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-filesystem'
      - /home/user/LibreChat/
    iconPath: /home/user/LibreChat/client/public/assets/logo.svg
  mcp-obsidian:
    command: npx
    args:
      - -y
      - 'mcp-obsidian'
      - /path/to/obsidian/vault
  streamable-http-example:
    type: streamable-http
    url: https://example.com/mcp
    headers:
      Authorization: 'Bearer ${API_TOKEN}'
    timeout: 30000
  per-user-crendentials-example:
    type: sse
    url: 'https//some.mcp/sse'
    headers:
      X-Custom-Auth-Token: '{{USER_API_KEY}}' # Placeholder for the user-provided API key, defined in `customUserVars` below.
    customUserVars:
      USER_API_KEY:
        title: 'Service API Key'
        description: "Your personal API key for this service. You can get it <a href='https://example.com/api-keys' target='_blank'>here</a>."
    serverInstructions: true
```

see: [MCP Servers Object Structure](/docs/configuration/librechat_yaml/object_structure/mcp_servers)

## speech

**Key:**

<OptionTable
  options={[
    [
      'speech',
      'Object',
      'Configures Text-to-Speech (TTS) and Speech-to-Text (STT) providers for the application.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    [
      'tts',
      'Object',
      'Text-to-Speech provider configurations (OpenAI, Azure OpenAI, ElevenLabs, LocalAI).',
      '',
    ],
    ['stt', 'Object', 'Speech-to-Text provider configurations (OpenAI, Azure OpenAI).', ''],
    ['speechTab', 'Object', 'Default UI settings for speech features.', ''],
  ]}
/>

Both `speech.tts` and `speech.stt` accept an `allowedAddresses` array of trusted private host:port exemptions. Speech requests enforce the default private-address block at connect time. See the detailed [Speech reference](/docs/configuration/librechat_yaml/object_structure/speech#ssrf-protection) for entry rules, proxy behavior, and examples.

see: [Speech Object Structure](/docs/configuration/librechat_yaml/object_structure/speech)

## turnstile

**Key:**

<OptionTable
  options={[
    [
      'turnstile',
      'Object',
      'Configures Cloudflare Turnstile for bot protection on registration and login forms.',
      '',
    ],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['siteKey', 'String', 'Your Cloudflare Turnstile site key (required).', ''],
    ['options', 'Object', 'Additional Turnstile widget options (optional).', ''],
  ]}
/>

see: [Turnstile Object Structure](/docs/configuration/librechat_yaml/object_structure/turnstile)

## transactions

**Key:**

<OptionTable
  options={[
    ['transactions', 'Object', 'Controls transaction logging and visibility features.', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[['enabled', 'Boolean', 'Enables or disables transaction logging. Default: true.', '']]}
/>

see: [Transactions Object Structure](/docs/configuration/librechat_yaml/object_structure/transactions)

## Additional links

- [Summarization Object Structure](/docs/configuration/librechat_yaml/object_structure/summarization)
- [AWS Bedrock Object Structure](/docs/configuration/librechat_yaml/object_structure/aws_bedrock)
- [Custom Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/custom_endpoint)
- [Azure OpenAI Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/azure_openai)
- [Assistants Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint)
- [Agents](/docs/configuration/librechat_yaml/object_structure/agents)
- [OCR Config Object Structure](/docs/configuration/librechat_yaml/object_structure/ocr)
- [Speech Object Structure](/docs/configuration/librechat_yaml/object_structure/speech)
- [Turnstile Object Structure](/docs/configuration/librechat_yaml/object_structure/turnstile)
- [Transactions Object Structure](/docs/configuration/librechat_yaml/object_structure/transactions)
- [Balance Object Structure](/docs/configuration/librechat_yaml/object_structure/balance)
- [Web Search Object Structure](/docs/configuration/librechat_yaml/object_structure/web_search)
- [Memory Object Structure](/docs/configuration/librechat_yaml/object_structure/memory)


# Interface Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/interface)

## Overview

The `interface` object allows for customization of various user interface elements within the application, including visibility and behavior settings for components such as menus, panels, and links. This section provides a detailed breakdown of the `interface` object structure.

These are fields under `interface`:

- `mcpServers`
- `privacyPolicy`
- `termsOfService`
- `modelSelect`
- `parameters`
- `contextUsage`
- `contextCost`
- `currency`
- `presets`
- `prompts`
- `bookmarks`
- `memories`
- `multiConvo`
- `agents`
- `remoteAgents`
- `skills`
- `sharedLinks`
- `schedules`
- `temporaryChat`
- `temporaryChatRetention`
- `retentionMode`
- `retainAgentFiles`
- `autoSubmitFromUrl`
- `customWelcome`
- `runCode`
- `webSearch`
- `fileSearch`
- `fileCitations`
- `feedback`
- `defaultPinnedTools`
- `peoplePicker`
- `marketplace`

**Notes:**

- The `interface` configurations are applied globally within the application.
- Default values are provided for most settings but can be overridden based on specific requirements or conditions.
- Conditional logic in the application can further modify these settings based on other configurations like model specifications.

<Callout type="warning" title="Deprecated: permission side-effect fields">
Several fields below (`mcpServers`, `prompts`, `bookmarks`, `memories`, `multiConvo`, `agents`, `remoteAgents`, `skills`, `sharedLinks`, `schedules`, `temporaryChat`, `runCode`, `webSearch`, `fileSearch`, `fileCitations`, `peoplePicker`, `marketplace`) don't just toggle UI, they seed role permissions in the database at startup, and only for the built-in `USER` role.

For ongoing management, use the [**LibreChat Admin Panel**](/docs/features/admin_panel), which edits the permission matrix directly on each role (including custom roles). These YAML fields remain supported for bootstrapping a fresh instance or fully file-driven deployments, but should no longer be used as the primary way to manage feature permissions.

See [Access Control](/docs/features/access_control) for the full permission model.

</Callout>

## Example

```yaml filename="interface"
interface:
  mcpServers:
    placeholder: 'MCP Servers'
    use: true
    create: true
    share: false
    public: false
    configureObo: false
    trustCheckbox:
      label: 'I trust this server'
      subLabel: 'Only enable servers you trust'
  privacyPolicy:
    externalUrl: 'https://example.com/privacy'
    openNewTab: true
  termsOfService:
    externalUrl: 'https://example.com/terms'
    openNewTab: true
    modalAcceptance: true
    modalTitle: 'Terms of Service'
    modalContent: |
      # Terms of Service
      ## Introduction
      Welcome to LibreChat!
  modelSelect: false
  parameters: true
  contextUsage: true
  contextCost: true
  currency:
    code: EUR
    rate: 0.92
  presets: false
  prompts:
    use: true
    create: true
    share: true
    public: false
  bookmarks: true
  multiConvo: true
  agents:
    use: true
    create: true
    share: true
    public: false
  skills:
    use: true
    create: true
    share: false
    public: false
    defaultActiveOnShare: false
  sharedLinks:
    create: true
    share: true
    public: false
    snapshotFiles: true
  schedules:
    use: true
    create: true
    maxPerUser: 10
    minIntervalMinutes: 60
    autoDisableAfterFailures: 5
    fireConcurrency: 5
  customWelcome: 'Hey {{user.name}}! Welcome to LibreChat'
  runCode: true
  webSearch: true
  fileSearch: true
  fileCitations: true
  feedback: true
  defaultPinnedTools:
    - artifacts
    - execute_code
    - mcp
```

## mcpServers

> **Deprecated for permission management.** The `use`, `create`, `share`, and `public` sub-keys seed role permissions at startup. Prefer the [Admin Panel](/docs/features/admin_panel) for managing MCP server permissions per role/group/user. The `placeholder` and `trustCheckbox` sub-keys are unaffected.

**Key:**

<OptionTable
  options={[
    [
      'mcpServers',
      'Object',
      'Contains settings related to the MCP (Model Context Protocol) server selection interface and access control.',
      'Allows for customization of the placeholder text, user permissions, and trust checkbox labels.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    [
      'placeholder',
      'String',
      'The placeholder text displayed in the MCP server selection dropdown when no server is selected.',
      'MCP Servers',
    ],
    [
      'use',
      'Boolean',
      'Controls whether users have permission to use existing MCP servers.',
      'true',
    ],
    [
      'create',
      'Boolean',
      'Controls whether users have permission to create new MCP servers.',
      'true',
    ],
    [
      'share',
      'Boolean',
      'Controls whether users have permission to share MCP servers with other users.',
      'false',
    ],
    [
      'public',
      'Boolean',
      'Controls whether users can share MCP servers publicly (visible to all users).',
      'false',
    ],
    [
      'configureObo',
      'Boolean',
      'Controls whether users have permission to configure MCP server On-Behalf-Of token exchange.',
      'false',
    ],
    [
      'trustCheckbox',
      'Object',
      'Customizable labels for the trust checkbox in the MCP server dialog. Supports simple strings or language-keyed objects for internationalization.',
      'See below',
    ],
  ]}
/>

**trustCheckbox Sub-keys:**

<OptionTable
  options={[
    [
      'label',
      'String or Object',
      'The main label for the trust checkbox. Can be a simple string or a language-keyed object (e.g., { en: "I trust this server", es: "Confío en este servidor" }).',
      '',
    ],
    [
      'subLabel',
      'String or Object',
      'The sub-label (help text) for the trust checkbox. Can be a simple string or a language-keyed object for internationalization.',
      '',
    ],
  ]}
/>

**Example:**

```yaml filename="interface / mcpServers"
interface:
  mcpServers:
    placeholder: 'Select MCP Server'
    use: true
    create: true
    share: false
    configureObo: false
    trustCheckbox:
      label:
        en: 'I trust this server'
        es: 'Confío en este servidor'
      subLabel:
        en: 'Only enable servers you trust'
        es: 'Solo habilite servidores en los que confíe'
```

## privacyPolicy

**Key:**

<OptionTable
  options={[
    [
      'privacyPolicy',
      'Object',
      'Contains settings related to the privacy policy link provided in the user interface.',
      'Allows for the specification of a custom URL and the option to open it in a new tab.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    ['externalUrl', 'String (URL)', 'The URL pointing to the privacy policy document.', ''],
    ['openNewTab', 'Boolean', 'Specifies whether the link should open in a new tab.', ''],
  ]}
/>

## termsOfService

**Key:**

<OptionTable
  options={[
    [
      'termsOfService',
      'Object',
      'Contains settings related to the terms of service link provided in the user interface.',
      'Allows for the specification of a custom URL and the option to open it in a new tab, as well as a modal acceptance dialog for the terms of service.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    [
      'externalUrl',
      'String (URL)',
      'The URL pointing to the terms of service document.',
      'https://librechat.ai/tos',
    ],
    ['openNewTab', 'Boolean', 'Specifies whether the link should open in a new tab.', 'true'],
    [
      'modalAcceptance',
      'Boolean',
      'Specifies whether to show a modal terms and conditions dialog for users to accept in order to be able to use LibreChat.',
      'true',
    ],
    [
      'modalTitle',
      'String',
      'Specifies a custom title for the modal terms and conditions dialog (optional).',
      'Terms of Service',
    ],
    [
      'modalContent',
      'String',
      'Specifies the content of the modal terms and conditions dialog in MarkDown format.',
      'See librechat.yaml.example for how to correctly format the multi-line parameter.',
    ],
  ]}
/>

When modal acceptance is enabled, LibreChat records both the acceptance state and the time it was accepted. After upgrading an existing deployment, run `npm run migrate:terms-timestamp` to backfill accepted users that do not have a timestamp. The migration asks for confirmation, processes all tenants, uses each user's account creation time as the fallback, and exits non-zero if any batch fails. Running `npm run reset-terms` clears both the acceptance state and its timestamp so users must accept again.

## modelSelect

**Key:**

<OptionTable
  options={[
    [
      'modelSelect',
      'Boolean',
      'Determines whether the model selection feature is available in the UI.',
      'Enabling this feature allows users to select different models directly from the interface.',
    ],
  ]}
/>

**Default:** `true`

**Notes:**

- This is required to be `true` if using [`modelSpecs.addedEndpoints`](/docs/configuration/librechat_yaml/object_structure/model_specs#addedendpoints).
- If `modelSpecs.addedEndpoints` is used and `interface.modelSelect` is not explicitly set, it defaults to `true`.

**Example:**

```yaml filename="interface / modelSelect"
interface:
  modelSelect: true
```

## parameters

**Key:**

<OptionTable
  options={[
    [
      'parameters',
      'Boolean',
      'Toggles the visibility of parameter configuration options within the interface.',
      'This setting is crucial for users who need to adjust parameters for specific functionalities within the application.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / parameters"
interface:
  parameters: false
```

## contextUsage

**Key:**

<OptionTable
  options={[
    [
      'contextUsage',
      'Boolean',
      'Shows or hides the real-time context window and token usage gauge in the conversation UI.',
      'When disabled, users will not see the context usage indicator for chats.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / contextUsage"
interface:
  contextUsage: true
```

## contextCost

**Key:**

<OptionTable
  options={[
    [
      'contextCost',
      'Boolean',
      'Shows or hides cost values in context and token usage details.',
      'When disabled, users can still see token usage if contextUsage is enabled, but cost values are hidden.',
    ],
  ]}
/>

**Default:** `false`

**Notes:**

- Set `contextCost: true` to opt in to visible cost values. Token usage can remain visible through `contextUsage` while cost values stay hidden.
- `currency` only affects displayed costs when cost display is enabled.

**Example:**

```yaml filename="interface / contextCost"
interface:
  contextCost: true
```

## currency

**Key:**

<OptionTable
  options={[
    [
      'currency',
      'Object',
      'Converts displayed usage costs from USD to another currency using a static conversion rate.',
      'Set code to the display currency and rate to a positive conversion multiplier from USD.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    ['code', 'String', 'Currency code shown in usage/cost displays.', 'USD'],
    ['rate', 'Number', 'Positive multiplier applied to USD usage costs.', '1'],
  ]}
/>

**Example:**

```yaml filename="interface / currency"
interface:
  currency:
    code: EUR
    rate: 0.92
```

## presets

**Key:**

<OptionTable
  options={[
    [
      'presets',
      'Boolean',
      "Enables or disables the use of presets in the application's UI.",
      'Presets can simplify user interactions by providing pre-configured settings or operations, enhancing user experience and efficiency.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / presets"
interface:
  presets: true
```

## prompts

> **Deprecated for permission management.** Seeds the `PROMPTS` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel) for managing prompt permissions per role/group/user.

**Key:**

<OptionTable
  options={[
    [
      'prompts',
      'Boolean or Object',
      'Controls prompt-related features for all users. Can be a boolean for simple enable/disable, or an object for granular control over use, creation, sharing, and public visibility.',
      'When set to `false`, users will not have access to create, edit, or use custom prompts.',
    ],
  ]}
/>

**Default:** `true`

**Important: Boolean vs Object Configuration**

- **Boolean (`prompts: true`)**: Only updates the `use` permission. Existing `create`, `share`, and `public` permission values are **preserved** from the database. Use this as a simple feature toggle without affecting other settings configured through the admin panel.

- **Object**: Updates only the sub-permissions that are explicitly specified. Any permissions not included in the config are preserved from the database.

When using the object structure:

**Sub-keys:**

<OptionTable
  options={[
    ['use', 'Boolean', 'Controls whether users can use prompts.', 'true'],
    ['create', 'Boolean', 'Controls whether users can create new prompts.', 'true'],
    [
      'share',
      'Boolean',
      'Controls whether users can share prompts with specific users/groups.',
      'false',
    ],
    [
      'public',
      'Boolean',
      'Controls whether users can share prompts publicly (visible to all users).',
      'false',
    ],
  ]}
/>

**Example (boolean - simple feature toggle):**

```yaml filename="interface / prompts (boolean)"
interface:
  prompts: true # Only updates USE; create/share/public remain unchanged
```

**Example (object - granular control):**

```yaml filename="interface / prompts (object)"
interface:
  prompts:
    use: true
    create: false # Disable creation while allowing use
    # share and public not specified - preserves existing values
```

**Example (object - full control):**

```yaml filename="interface / prompts (object)"
interface:
  prompts:
    use: true
    create: true
    share: true
    public: false
```

## bookmarks

> **Deprecated for permission management.** Seeds the `BOOKMARKS` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    [
      'bookmarks',
      'Boolean',
      'Enables or disables all bookmarks-related features for all users.',
      'When disabled, users will not be able to create, manage, or access bookmarks within the application.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / bookmarks"
interface:
  bookmarks: true
```

## memories

> **Deprecated for permission management.** Seeds the `MEMORIES` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel). Note this toggle is separate from the [`memory`](/docs/configuration/librechat_yaml/object_structure/memory) behavior configuration.

**Key:**

<OptionTable
  options={[
    [
      'memories',
      'Boolean',
      'Enables or disables the memories feature for all users in the interface.',
      'When disabled, users will not have access to the memories panel or memory-related features.',
    ],
  ]}
/>

**Default:** `true`

**Note:** This controls the UI visibility of the memories feature. For detailed memory behavior configuration (token limits, personalization, agent settings), see the [Memory Configuration](/docs/configuration/librechat_yaml/object_structure/memory).

**Example:**

```yaml filename="interface / memories"
interface:
  memories: true
```

## multiConvo

> **Deprecated for permission management.** Seeds the `MULTI_CONVO` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    [
      'multiConvo',
      'Boolean',
      'Enables or disables all "multiConvo", AKA multiple response streaming, related features for all users.',
      'When disabled, users will not be able to stream responses from 2 AI models at the same time.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / multiConvo"
interface:
  multiConvo: true
```

## agents

More info on [Agents](/docs/features/agents)

> **Deprecated for permission management.** Seeds the `AGENTS` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel) for managing agent permissions per role/group/user.

**Key:**

<OptionTable
  options={[
    [
      'agents',
      'Boolean or Object',
      'Controls agent-related features for all users. Can be a boolean for simple enable/disable, or an object for granular control over use, creation, sharing, and public visibility.',
      'When set to `false`, users will not have access to agents.',
    ],
  ]}
/>

**Default:** `true`

**Important: Boolean vs Object Configuration**

- **Boolean (`agents: true`)**: Only updates the `use` permission. Existing `create`, `share`, and `public` permission values are **preserved** from the database. Use this as a simple feature toggle without affecting other settings configured through the admin panel.

- **Object**: Updates only the sub-permissions that are explicitly specified. Any permissions not included in the config are preserved from the database.

When using the object structure:

**Sub-keys:**

<OptionTable
  options={[
    ['use', 'Boolean', 'Controls whether users can use agents.', 'true'],
    ['create', 'Boolean', 'Controls whether users can create new agents.', 'true'],
    [
      'share',
      'Boolean',
      'Controls whether users can share agents with specific users/groups.',
      'false',
    ],
    [
      'public',
      'Boolean',
      'Controls whether users can share agents publicly (visible to all users).',
      'false',
    ],
  ]}
/>

**Example (boolean - simple feature toggle):**

```yaml filename="interface / agents (boolean)"
interface:
  agents: true # Only updates USE; create/share/public remain unchanged
```

**Example (object - granular control):**

```yaml filename="interface / agents (object)"
interface:
  agents:
    use: true
    create: false # Disable creation while allowing use
    # share and public not specified - preserves existing values
```

**Example (object - full control):**

```yaml filename="interface / agents (object)"
interface:
  agents:
    use: true
    create: true
    share: true
    public: false
```

## remoteAgents

Controls access to the Agents API (OpenAI-compatible and Open Responses API endpoints), which allows external applications to interact with LibreChat agents programmatically via API keys.

> **Deprecated for permission management.** Seeds the `REMOTE_AGENTS` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    [
      'remoteAgents',
      'Object',
      'Configuration for remote agent API access control. All fields default to `false`.',
      '',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    ['use', 'Boolean', 'Controls whether users can access the remote agents API.', 'false'],
    ['create', 'Boolean', 'Controls whether users can create API keys for remote agents.', 'false'],
    ['share', 'Boolean', 'Controls whether users can share remote agents.', 'false'],
    ['public', 'Boolean', 'Controls whether users can share remote agents publicly.', 'false'],
  ]}
/>

**Default:** All fields default to `false` (disabled).

**Example:**

```yaml filename="interface / remoteAgents"
interface:
  remoteAgents:
    use: true
    create: true
    share: false
    public: false
```

**Note:** Admin users have all remote agent permissions enabled by default regardless of this configuration.

## skills

> **Deprecated for permission management.** Seeds the `SKILLS` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel) for managing Skills permissions per role/group/user. `defaultActiveOnShare` is not a permission bit and remains a YAML behavior setting.

**Key:**

<OptionTable
  options={[
    [
      'skills',
      'Boolean or Object',
      'Controls Skills-related permissions and shared-skill activation defaults. Can be a boolean for simple enable/disable, or an object for granular control.',
      'When set to `false`, users cannot use, create, or share Skills.',
    ],
  ]}
/>

**Default:** `true` for `use` and `create`, `false` for `share`, `public`, and `defaultActiveOnShare`.

**Sub-keys:**

<OptionTable
  options={[
    ['use', 'Boolean', 'Controls whether users can use Skills.', 'true'],
    ['create', 'Boolean', 'Controls whether users can create Skills.', 'true'],
    [
      'share',
      'Boolean',
      'Controls whether users can share Skills with specific users/groups.',
      'false',
    ],
    [
      'public',
      'Boolean',
      'Controls whether users can share Skills publicly (visible to all users).',
      'false',
    ],
    [
      'defaultActiveOnShare',
      'Boolean',
      'Controls whether shared Skills default to active for recipients until they override the setting.',
      'false',
    ],
  ]}
/>

**Example:**

```yaml filename="interface / skills"
interface:
  skills:
    use: true
    create: true
    share: false
    public: false
    defaultActiveOnShare: false
```

For Skills behavior and invocation modes, see [Skills](/docs/features/skills).

## sharedLinks

> **Deprecated for permission management.** Seeds the `SHARED_LINKS` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel) for managing shared-link permissions per role/group/user.

**Key:**

<OptionTable
  options={[
    [
      'sharedLinks',
      'Boolean or Object',
      'Controls shared-link permissions. Can be a boolean to enable/disable all shared-link permissions, or an object for granular create/share/public control.',
      'When set to `false`, users cannot create or share conversation links.',
    ],
  ]}
/>

**Default:** `create: true`, `share: true`, `public: true`

**Important: Boolean vs Object Configuration**

- **Boolean (`sharedLinks: true`)**: Enables all `SHARED_LINKS` permissions for the default `USER` role.
- **Boolean (`sharedLinks: false`)**: Disables all `SHARED_LINKS` permissions for the default `USER` role.
- **Object**: Updates only the sub-permissions that are explicitly specified. Any permissions not included in the config are preserved from the database.

**Sub-keys:**

<OptionTable
  options={[
    ['create', 'Boolean', 'Controls whether users can create shared conversation links.', 'true'],
    [
      'share',
      'Boolean',
      'Controls whether users can share links with authenticated users.',
      'true',
    ],
    [
      'public',
      'Boolean',
      'Controls whether users can toggle "share with everyone" for a shared link. Anonymous viewing still requires ALLOW_SHARED_LINKS_PUBLIC=true.',
      'true',
    ],
    [
      'snapshotFiles',
      'Boolean',
      'Controls whether newly created shared links can snapshot referenced conversation files so recipients can preview or download them through the link.',
      'true',
    ],
  ]}
/>

**Example:**

```yaml filename="interface / sharedLinks"
interface:
  sharedLinks:
    create: true
    share: true
    public: false
    snapshotFiles: true
```

For user-facing shared-link behavior, see [Shareable Links](/docs/features/shareable_links).

## schedules

Enables the experimental [Scheduled Chats](/docs/features/scheduled_chats) panel and configures its permissions and runtime limits.

> **Deprecated for permission management.** The `use` and `create` sub-keys seed the `SCHEDULES` permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel) for ongoing permission management. The runtime limit fields remain YAML settings.

<Callout type="warning" title="Experimental and default-off">
  The `schedules` field is absent from LibreChat's default interface configuration. Scheduled runs
  can consume model and tool resources, so add this field only after reviewing the deployment
  requirements in [Scheduled Chats](/docs/features/scheduled_chats#deployment-safety).
</Callout>

**Key:**

<OptionTable
  options={[
    [
      'schedules',
      'Boolean or Object',
      'Controls Scheduled Chats permissions and deployment limits.',
      'Omitted by default (disabled).',
    ],
  ]}
/>

**Boolean behavior:**

- `schedules: true` opts in with the default runtime limits without changing stored role permissions.
- `schedules: false` is a deployment-wide stop. It cannot be re-enabled by a role, group, or user configuration override.
- Object form opts in unless `use: false` is set.

**Sub-keys:**

<OptionTable
  options={[
    ['use', 'Boolean', 'Controls whether users can list and view their schedules.', 'true when opted in'],
    [
      'create',
      'Boolean',
      'Controls whether users can create, edit, enable, run, and delete schedules.',
      'true',
    ],
    ['maxPerUser', 'Number', 'Maximum schedules per user. Set to 0 to prevent creation.', '10'],
    ['minIntervalMinutes', 'Number', 'Shortest allowed interval between occurrences.', '60'],
    [
      'autoDisableAfterFailures',
      'Number',
      'Consecutive failed runs before a schedule is disabled.',
      '5',
    ],
    [
      'fireConcurrency',
      'Number',
      'Maximum scheduled runs admitted concurrently across the deployment.',
      '5',
    ],
    [
      'requireProject',
      'Boolean',
      'Requires every schedule to resolve to an owned Chat Project at create/update time and again at each run.',
      'false',
    ],
    [
      'projectId',
      'String',
      'Pins every scheduled conversation to one Chat Project, overriding the stored choice and implying `requireProject`.',
      '',
    ],
  ]}
/>

```yaml filename="interface / schedules"
interface:
  schedules:
    use: true
    create: true
    maxPerUser: 10
    minIntervalMinutes: 60
    autoDisableAfterFailures: 5
    fireConcurrency: 5
    requireProject: false
    # projectId: '000000000000000000000000'
```

The base YAML is authoritative for the global enable state. Configuration overrides can narrow access and tune inherited limits, but cannot re-enable a base `false` or `{ use: false }`. `SCHEDULES_DISABLED=true` is an additional emergency stop for both automatic and manual runs.

`requireProject` is enforced both when a schedule is written and immediately before each run. A schedule that no longer satisfies the requirement is auto-disabled rather than starting an unscoped conversation. `projectId` outranks a schedule's stored selection and is resolved as the schedule owner; because projects are user-owned, it is primarily useful in per-role or per-user configuration overrides rather than as one deployment-wide value.

## temporaryChat

Controls whether the temporary chat feature is available to users. Temporary chats are not saved to conversation history and are automatically deleted after a configurable retention period.

> **Deprecated for permission management.** Seeds the `TEMPORARY_CHAT` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel). `temporaryChatRetention` below is not a permission and remains the recommended way to configure retention.

**Key:**

<OptionTable
  options={[
    [
      'temporaryChat',
      'Boolean',
      'Enables or disables the temporary chat feature.',
      'When set to `false`, users will not see the option to start temporary chats.',
    ],
  ]}
/>

**Default:** `true`

**Note:** The retention period for temporary chats can be configured using `temporaryChatRetention`.

**Example:**

```yaml filename="interface / temporaryChat"
interface:
  temporaryChat: true
```

## temporaryChatRetention

The `temporaryChatRetention` configuration allows you to customize how long temporary chats are retained before being automatically deleted.

**Key:**

<OptionTable
  options={[
    [
      'temporaryChatRetention',
      'Number',
      'Sets the retention period for temporary chats in hours.',
      'temporaryChatRetention: 168',
    ],
  ]}
/>

**Validation Rules:**

- **Minimum**: 1 hour (prevents immediate deletion)
- **Maximum**: 8760 hours (1 year maximum retention)
- **Default**: 720 hours (30 days)

**Configuration Methods:**

1. **LibreChat.yaml** (recommended): `interface.temporaryChatRetention: 168`
2. **Environment Variable** (deprecated): `TEMP_CHAT_RETENTION_HOURS=168`

> **Note:** The environment variable `TEMP_CHAT_RETENTION_HOURS` is deprecated. Please use the `interface.temporaryChatRetention` config option in `librechat.yaml` instead. The config file value takes precedence over the environment variable.

**Example:**

```yaml filename="interface / temporaryChatRetention"
interface:
  temporaryChatRetention: 168 # Retain temporary chats for 7 days
  retentionMode: 'temporary'
```

**Common Retention Periods:**

- **1 hour**: `temporaryChatRetention: 1` (minimal retention)
- **24 hours**: `temporaryChatRetention: 24` (1 day)
- **168 hours**: `temporaryChatRetention: 168` (1 week)
- **720 hours**: `temporaryChatRetention: 720` (30 days - default)
- **8760 hours**: `temporaryChatRetention: 8760` (1 year - maximum)

## retentionMode

Controls which data receives retention deadlines.

**Key:**

<OptionTable
  options={[
    [
      'retentionMode',
      'String',
      'Set to "temporary" to apply retention only to temporary chats, or "all" to apply retention to all supported retained data, including persistent agent resource files unless retainAgentFiles is true.',
      'retentionMode: "temporary"',
    ],
  ]}
/>

**Default:** `temporary`

<Callout type="warning">
  `retentionMode: "all"` applies retention deadlines beyond temporary chats, including persistent
  agent resource files unless `retainAgentFiles: true` is configured. Confirm your retention policy
  before enabling it.
</Callout>

**Example:**

```yaml filename="interface / retentionMode"
interface:
  temporaryChatRetention: 168
  retentionMode: 'all'
```

## retainAgentFiles

Controls whether persistent agent resource files are exempt from all-data retention.

**Key:**

<OptionTable
  options={[
    [
      'retainAgentFiles',
      'Boolean',
      'When true, persistent agent resource files do not expire under retentionMode: "all". Non-agent files and message attachments still expire.',
      'retainAgentFiles: false',
    ],
  ]}
/>

**Default:** `false`

**Notes:**

- This setting only changes behavior when `retentionMode` is set to `"all"`.
- Set this to `true` when agents should keep their persistent resource files even while conversations, messages, and non-agent files receive retention deadlines.

**Example:**

```yaml filename="interface / retainAgentFiles"
interface:
  temporaryChatRetention: 168
  retentionMode: 'all'
  retainAgentFiles: true
```

## autoSubmitFromUrl

Controls whether a prompt supplied via URL query parameters on `/c/new` is auto-submitted to the model.

When `/c/new?prompt=…&submit=true` is opened by an authenticated user, LibreChat normally pre-fills the composer with the URL-supplied prompt and submits it immediately. This is a convenience feature for crafted deeplinks and shared chat URLs.

For deployments where users may receive crafted links from external sources — and where memory- or tool-enabled models could leak sensitive context if a prompt-injection payload reaches the model — operators can disable auto-submission. With the flag set to `false`, the prompt is still pre-filled in the composer but the user must press **Send** explicitly.

**Key:**

<OptionTable
  options={[
    [
      'autoSubmitFromUrl',
      'Boolean',
      'Controls whether `/c/new?prompt=…&submit=true` auto-submits to the model.',
      'When `false`, the prompt is pre-filled in the composer but not submitted.',
    ],
  ]}
/>

**Default:** `true` (existing behavior is preserved unless explicitly disabled).

**Notes:**

- This setting does not affect URL-driven model spec selection or other URL-driven settings — only the auto-submission step.
- The query parameter accepts both `prompt` and `q` as the prompt source, with `prompt` taking precedence. `submit=true` is the trigger.
- Recommended for instances handling sensitive memory or tool data, where a 1-click prompt-injection vector should require explicit user confirmation.

**Example:**

```yaml filename="interface / autoSubmitFromUrl"
interface:
  autoSubmitFromUrl: false
```

## customWelcome

**Key:**

<OptionTable
  options={[
    [
      'customWelcome',
      'String',
      'Allows administrators to define a custom welcome message for the chat interface, with the option to personalize it using the {{user.name}} parameter.',
    ],
  ]}
/>

**Default:** _None (if not specified, a default greeting is used)_

**Example:**

```yaml filename="interface / customWelcome"
interface:
  customWelcome: 'Hey {{user.name}}! Welcome to LibreChat'
```

**Note:** You can use `{{user.name}}` within the `customWelcome` message to dynamically insert the user's name for a personalized greeting experience.

## runCode

Enables/disables the "Run Code" button for Markdown Code Blocks. More info on the [LibreChat Code Interpreter API](/docs/features/code_interpreter)

**Note:** This setting does not disable the [Agents Code Interpreter Capability](/docs/features/agents#code-interpreter). To disable the Agents Capability, see the [Agents Endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents) instead.

> **Deprecated for permission management.** Seeds the `RUN_CODE` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    ['runCode', 'Boolean', 'Enables or disables the "Run Code" button for Markdown Code Blocks.'],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / runCode"
interface:
  runCode: true
```

## webSearch

Enables/disables the web search button in the chat interface. More info on [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search)

**Note:** This setting does not disable the [Agents Web Search capability](/docs/features/agents#agent-capabilities). To disable the Agents capability, see the [Agents endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) instead.

> **Deprecated for permission management.** Seeds the `WEB_SEARCH` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    ['webSearch', 'Boolean', 'Enables or disables the web search button in the chat interface.'],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / webSearch"
interface:
  webSearch: true
```

## fileSearch

Enables/disables the file search (for RAG API usage via tool) button in the chat interface

**Note:** This setting does not disable the [Agents File Search Capability](/docs/features/agents#file-search). To disable the Agents Capability, see the [Agents Endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents) instead.

> **Deprecated for permission management.** Seeds the `FILE_SEARCH` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    ['fileSearch', 'Boolean', 'Enables or disables the file search button in the chat interface.'],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / fileSearch"
interface:
  fileSearch: true
```

## fileCitations

Controls the global availability of file citations functionality. When disabled, it effectively removes the `FILE_CITATIONS` permission for all users, preventing any file citations from being displayed when using file search, regardless of individual user permissions.

> **Deprecated for permission management.** Seeds/globally gates the `FILE_CITATIONS` role permission at startup. Prefer the [Admin Panel](/docs/features/admin_panel) for managing citations permissions per role/group/user.

**Note:**

- This setting acts as a global toggle for the `FILE_CITATIONS` permission system-wide.
- When set to `false`, no users will see file citations, even if they have been granted the permission through roles.
- File citations require the `fileSearch` feature to be enabled.
- When using agents with file search capability, citation behavior (quantity and quality) can be configured through the [Agents endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents#file-citation-configuration-examples).

**Key:**

<OptionTable
  options={[
    [
      'fileCitations',
      'Boolean',
      'Globally enables or disables the FILE_CITATIONS permission for all users, controlling whether file search results can include source citations.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / fileCitations"
interface:
  fileCitations: true
```

## feedback

Controls whether the thumbs up / thumbs down buttons are shown under responses. When set to `false`, the buttons are removed from the message action row and the server rejects feedback writes, so a deployment that hides the controls also stores no ratings.

**Notes:**

- The other message actions (read aloud, copy, edit, fork, and regenerate) are unaffected, and the action row reflows without leaving a gap.
- With feedback disabled, `PUT /api/messages/:conversationId/:messageId/feedback` responds with `403` and `{"error": "Feedback is disabled"}` before anything is written to the database or exported.
- Ratings collected while the feature was enabled stay on existing messages. The flag stops new writes, it does not delete stored feedback.
- Ratings are only consumed outside LibreChat when Langfuse tracing is configured, where each one is sent as a `user-feedback` score. See [Message Feedback Scores](/docs/configuration/langfuse#message-feedback-scores).
- This is a plain interface flag rather than a role permission, so there is nothing to grant per role in the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    [
      'feedback',
      'Boolean',
      'Shows or hides the thumbs up/thumbs down feedback buttons on responses.',
      'When disabled, the buttons are removed for every user and the feedback endpoint rejects writes with a 403.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="interface / feedback"
interface:
  feedback: false
```

## defaultPinnedTools

Seeds the initial prompt-bar pinned tools for users who have not customized their pinned tool state. Once a user pins or unpins a tool, LibreChat preserves that user's choice.

**Key:**

<OptionTable
  options={[
    [
      'defaultPinnedTools',
      'Array of strings',
      'Tool keys and MCP dropdown/server names that should start pinned in the prompt bar for new or uncustomized users.',
      'When omitted, built-in tools start unpinned and the MCP dropdown keeps its default pinned state.',
    ],
  ]}
/>

**Supported values:**

- Built-in tool keys: `artifacts`, `execute_code`, `web_search`, `file_search`, `skills`
- `mcp` to pin the MCP servers dropdown
- A specific MCP server name to seed that server as pinned

**Example:**

```yaml filename="interface / defaultPinnedTools"
interface:
  defaultPinnedTools:
    - artifacts
    - execute_code
    - mcp
```

## peoplePicker

Controls which principal types (users, groups, roles) are available for selection in the people picker interface, typically used when sharing agents or managing access controls.

> **Deprecated for permission management.** Seeds the `PEOPLE_PICKER` role permissions at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[
    [
      'peoplePicker',
      'Object',
      'Configuration for which principal types are available in the people picker interface.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    ['users', 'Boolean', 'Enables user search in the people picker. Default: true'],
    ['groups', 'Boolean', 'Enables group search in the people picker. Default: true'],
    ['roles', 'Boolean', 'Enables role search in the people picker. Default: true'],
  ]}
/>

**Default:**

```yaml
peoplePicker:
  users: true
  groups: true
  roles: true
```

**Example:**

```yaml filename="interface / peoplePicker"
interface:
  peoplePicker:
    users: true
    groups: true
    roles: false # Disable role selection in people picker
```

## marketplace

Enables/disables access to the Agent Marketplace.

> **Deprecated for permission management.** Seeds the `MARKETPLACE` role permission at startup for the default `USER` role only. Prefer the [Admin Panel](/docs/features/admin_panel).

**Key:**

<OptionTable
  options={[['marketplace', 'Object', 'Configuration for Agent Marketplace access control.']]}
/>

**Sub-keys:**

<OptionTable
  options={[['use', 'Boolean', 'Enables or disables marketplace access. Default: false']]}
/>

**Default:**

```yaml
marketplace:
  use: false
```

**Example:**

```yaml filename="interface / marketplace"
interface:
  marketplace:
    use: true # Enable marketplace access
```


# Registration Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/registration)

## Example

```yaml filename="Registration Object Structure"
# Example Registration Object Structure
registration:
  socialLogins: ["google", "facebook", "github", "discord", "openid"]
  allowedDomains:
    - "gmail.com"
    - "protonmail.com"
```

## socialLogins

**Key:**
<OptionTable
  options={[
    ['socialLogins', 'Array of Strings', 'Defines the available social login providers and their display order.', 'The order of the providers in the list determines their appearance order on the login/registration page. Each provider listed must be properly configured within the system to be active and available for users. This configuration allows for a tailored authentication experience, emphasizing the most relevant or preferred social login options for your user base.'],
  ]}
/>


**Example:**
```yaml filename="registration / socialLogins"
socialLogins: ["google", "facebook", "github", "discord", "openid"]
```

## allowedDomains

**Key:**
<OptionTable
  options={[
    ['allowedDomains', 'Array of Strings', 'A list specifying allowed email domains for registration.', 'Users with email domains not listed will be restricted from registering.'],
  ]}
/>

**Required**

**Example:**
```yaml filename="registration / allowedDomains"
allowedDomains:
  - "gmail.com"
  - "protonmail.com"
```

# Cloudflare Turnstile Configuration (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/turnstile)

## Example

```yaml filename="turnstile"
turnstile:
  siteKey: "your-site-key-here"
  options:
    language: "auto"    # "auto" or an ISO 639-1 language code (e.g., en)
    size: "normal"      # Options: "normal", "compact", "flexible", or "invisible"
```

## turnstile

**Key:**
<OptionTable
  options={[
    ['turnstile', 'Object', 'Cloudflare Turnstile configuration that integrates a CAPTCHA alternative to protect your application from automated abuse.'],
  ]}
/>

### Fields

<OptionTable
  options={[
    ['siteKey', 'String', 'Your unique Cloudflare Turnstile site key. Register your domain with Cloudflare and obtain this key.', 'your-site-key-here'],
    ['options', 'Object', 'An object to customize additional settings for the Turnstile widget.'],
  ]}
/>

#### siteKey

- **Type:** `String`
- **Description:** Your unique Cloudflare Turnstile site key. Make sure you have registered your domain with Cloudflare and obtained this key from the [Cloudflare Turnstile Get Started](https://developers.cloudflare.com/turnstile/get-started/) guide.
- **Example:**
```yaml
turnstile:
  siteKey: "your-site-key-here"
```

#### options

- **Type:** `Object`
- **Description:** An object to configure additional settings for the Turnstile widget.

**Subkeys:**
<OptionTable
  options={[
    ['language', 'String', 'Specifies the language for the Turnstile widget. Use `auto` to automatically detect the user\'s language, or provide an ISO 639-1 language code (e.g., `en`).', 'auto'],
    ['size', 'String', 'Determines the widget\'s display size. Valid options include `normal`, `compact`, `flexible`, or `invisible`.', 'normal'],
    ]}
/>

```yaml
turnstile:
  options:
    language: "auto"
    size: "normal"
```

### Notes

- **Optional Integration:** The `turnstile` configuration block is optional. If you choose not to use Cloudflare Turnstile, you may omit this block entirely.
- **Dashboard Consistency:** Ensure that the values you configure here match your settings in the Cloudflare dashboard.
- **User Experience:** Customize the `options` subkeys as needed to tailor the widget's behavior and appearance to your application’s requirements.

---


# Model Specs Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/model_specs)

## **Overview**

The `modelSpecs` object helps you provide a simpler UI experience for AI models within your application.

There are 3 main fields under `modelSpecs`:

- `enforce` (optional; default: false)
- `prioritize` (optional; default: true)
- `list` (required)
- `addedEndpoints` (optional)

**Notes:**

- If `enforce` is set to true, model specifications can potentially conflict with other interface settings such as `modelSelect`, `presets`, and `parameters`.
- The `list` array contains detailed configurations for each model, including presets that dictate specific behaviors, appearances, and capabilities.
- If [interface](interface.mdx) fields are not specified in the configuration, having a list of model specs will disable the following interface elements:
  - `modelSelect`
  - `parameters`
  - `presets`
- If you would like to enable these interface elements along with model specs, you can set them to `true` in the `interface` object.

<Callout type="info" title="Managing user-provided API keys with Model Specs">
  When Model Specs disable `modelSelect`, the endpoints dropdown — and the gear icon that opens the **Set API Key** dialog — is hidden. Users can still set or rotate keys for any endpoint configured with `apiKey: "user_provided"` from **Settings → Data controls → API keys**.

  That list is scoped to the endpoints a user can actually reach: the endpoints referenced by your model specs, plus any [`addedEndpoints`](#addedendpoints). When the `agents` endpoint is reachable, it also includes the agent [`allowedProviders`](/docs/configuration/librechat_yaml/object_structure/agents#allowedproviders) (or every configured provider when `allowedProviders` is left unset).
</Callout>

## Example

```yaml filename="modelSpecs"
modelSpecs:
  enforce: true
  prioritize: true
  list:
    - name: 'meeting-notes-gpt4'
      label: 'Meeting Notes Assistant (GPT4)'
      softDefault: true
      description: 'Generate meeting notes by simply pasting in the transcript from a Teams recording.'
      iconURL: 'https://example.com/icon.png'
      showOnLanding: true
      conversation_starters:
        - 'Summarize this meeting transcript'
        - 'Extract action items and owners'
      hideBadgeRow: true
      skills:
        - 'brand-guidelines'
        - 'meeting-notes'
      subagents:
        enabled: true
        allowSelf: true
        agent_ids: []
      preset:
        endpoint: 'azureOpenAI'
        model: 'gpt-4-turbo-1106-preview'
        maxContextTokens: 128000 # Maximum context tokens
        max_tokens: 4096 # Maximum output tokens
        temperature: 0.2
        modelLabel: 'Meeting Summarizer'
        greeting: |
          This assistant creates meeting notes based on transcripts of Teams recordings.
          To start, simply paste the transcript into the chat box.
        promptPrefix: |
          Based on the transcript, create coherent meeting minutes for a business meeting. Include the following sections:
          - Date and Attendees
          - Agenda
          - Minutes
          - Action Items

          Focus on what items were discussed and/or resolved. List any open action items.
          The format should be a bulleted list of high level topics in chronological order, and then one or more concise sentences explaining the details.
          Each high level topic should have at least two sub topics listed, but add as many as necessary to support the high level topic. 

          - Do not start items with the same opening words.

          Take a deep breath and be sure to think step by step.
```

---

## **Top-level Fields**

### enforce

<OptionTable
  options={[
    [
      'enforce',
      'Boolean',
      'Determines whether the model specifications should strictly override other configuration settings.',
      'Setting this to `true` can lead to conflicts with interface options if not managed carefully.',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml filename="modelSpecs / enforce"
modelSpecs:
  enforce: true
```

---

### prioritize

<OptionTable
  options={[
    [
      'prioritize',
      'Boolean',
      'Specifies if model specifications should take priority over the default configuration when both are applicable.',
      'When set to `true`, it ensures that a modelSpec is always selected in the UI. Doing this may prevent users from selecting different endpoints for the selected spec.',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml filename="modelSpecs / prioritize"
modelSpecs:
  prioritize: false
```

---

### addedEndpoints

<OptionTable
  options={[
    [
      'addedEndpoints',
      'Array of Strings',
      'Allows specific endpoints (e.g., "openAI", "google") to be selectable in the UI alongside the defined model specs.',
      'Requires `interface.modelSelect` to be `true`. If this field is used and `interface.modelSelect` is not explicitly set, `modelSelect` will default to `true`.',
    ],
  ]}
/>

**Default:** `[]` (empty list)

**Note:** Must be one of the following:

- `openAI, azureOpenAI, google, anthropic, assistants, azureAssistants, bedrock, agents`

**Example:**

```yaml filename="modelSpecs / addedEndpoints"
modelSpecs:
  # ... other modelSpecs fields
  addedEndpoints:
    - openAI
    - google
```

---

### list

**Required**

<OptionTable
  options={[
    [
      'list',
      'Array of Objects',
      'Contains a list of individual model specifications detailing various configurations and behaviors.',
      "Each object in the list details the configuration for a specific model, including its behaviors, appearance, and capabilities related to the application's functionality.",
    ],
  ]}
/>

## **Model Spec (List Item)**

Within each **Model Spec**, or each **list** item, you can configure the following fields:

---

### name

<OptionTable
  options={[
    ['name', 'String', 'Unique identifier for the model.', 'No default. Must be specified.'],
  ]}
/>

**Description:**
Unique identifier for the model.

---

### label

<OptionTable
  options={[
    [
      'label',
      'String',
      'A user-friendly name or label for the model, shown in the header dropdown.',
      'No default. Optional.',
    ],
  ]}
/>

**Description:**
A user-friendly name or label for the model, shown in the header dropdown.

---

### default

<OptionTable
  options={[
    [
      'default',
      'Boolean',
      'Specifies if this model spec is the default selection, to be auto-selected on every new chat.',
      '',
    ],
  ]}
/>

**Description:**  
Specifies if this model spec is the default selection, to be auto-selected on every new chat.

---

### softDefault

<OptionTable
  options={[
    [
      'softDefault',
      'Boolean',
      'Specifies if this model spec should be selected only for first-time users who have not already selected a model, model spec, or agent.',
      '',
    ],
  ]}
/>

**Description:**

Specifies a first-run default without overriding a user's later selections. Use `softDefault` when you want to guide new users to a curated spec while preserving user choice after they pick another model, spec, or agent.

Viewing an older conversation that used a soft-default spec does not re-arm that spec as the user's default after they have made another selection.

If the user's stored selection points to an Agent that no longer resolves, LibreChat re-arms the soft default so a new chat still has a usable selection. A temporary failure to load the Agent list does not clear the stored choice or trigger that fallback.

**Example:**

```yaml filename="modelSpecs / softDefault"
modelSpecs:
  list:
    - name: 'general-assistant'
      label: 'General Assistant'
      softDefault: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### showInMenu

<OptionTable
  options={[
    [
      'showInMenu',
      'Boolean',
      'Controls whether the complete model spec appears in the model selector and client startup configuration.',
      'true',
    ],
  ]}
/>

Set `showInMenu: false` to hide a spec from interactive selection while keeping it available for trusted server-side callers that explicitly send its `name` in the `spec` field. This differs from `showIconInMenu`, which hides only the icon.

```yaml filename="modelSpecs / showInMenu"
modelSpecs:
  list:
    - name: 'api-only-assistant'
      label: 'API-only Assistant'
      showInMenu: false
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### iconURL

<OptionTable
  options={[
    [
      'iconURL',
      'String',
      "URL or a predefined endpoint name for the model's icon in selector, header, and conversation branding.",
      'No default. Optional.',
    ],
  ]}
/>

**Description:**  
URL or a predefined endpoint name for the model's icon in selector, header, and conversation branding. Use `showIconInMenu` and `showIconInHeader` to control where the icon appears.

---

### description

<OptionTable
  options={[
    [
      'description',
      'String',
      'A brief description of the model and its intended use or role, shown in the model selector and optionally on the chat landing.',
      'No default. Optional.',
    ],
  ]}
/>

**Description:**
A brief description of the model and its intended use or role, shown in the model selector. If `showOnLanding` is true, the same description is also shown on the chat landing under the spec label.

Plain text descriptions render as text. Descriptions that start with `<` render through the config HTML sanitizer, allowing safe inline markup and media such as small icons.

---

### conversation_starters

<OptionTable
  options={[
    [
      'conversation_starters',
      'Array of Strings',
      'Suggested starter prompts shown as clickable cards on the chat landing when this model spec is selected.',
      'No default. Optional.',
    ],
  ]}
/>

**Description:**
Conversation starters give users curated first prompts for a model spec. They are shown on the empty chat landing for the selected spec and are especially useful with `showOnLanding` branding. Clicking a starter submits it as the first message of a new conversation.

- A maximum of 4 starters are displayed, matching the agent/assistant limit.
- If the spec's preset points to an agent or assistant that defines its own conversation starters, those take precedence.

**Example:**

```yaml filename="modelSpecs / conversation_starters"
modelSpecs:
  list:
    - name: 'meeting-notes'
      label: 'Meeting Notes'
      showOnLanding: true
      conversation_starters:
        - 'Summarize this meeting transcript'
        - 'Create action items with owners and due dates'
      preset:
        endpoint: 'agents'
        model: 'gpt-4o'
```

---

### showOnLanding

<OptionTable
  options={[
    [
      'showOnLanding',
      'Boolean',
      "Shows this model spec's label and description on the chat landing in place of the default greeting.",
      'showOnLanding: true',
    ],
  ]}
/>

**Default:** `false`

Use this when a curated model spec should brand the first empty-chat screen. Existing model specs are unchanged unless `showOnLanding` is set to `true`.

**Example:**

```yaml filename="modelSpecs / showOnLanding"
modelSpecs:
  list:
    - name: 'branded-assistant'
      label: 'Acme Research'
      description: '<span><img src="/assets/acme.svg" alt="Acme" /> Research with approved sources</span>'
      showOnLanding: true
      iconURL: '/assets/acme.svg'
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### group

<OptionTable
  options={[
    [
      'group',
      'String',
      'Optional group name for organizing model specs in the UI selector. Controls where the spec appears in the menu hierarchy.',
      'No default. Optional.',
    ],
    [
      'groupIcon',
      'String',
      'Optional icon for custom groups. Can be a URL or a built-in endpoint key (e.g., "openAI", "groq"). Only the first spec with a groupIcon in each group is used.',
      'No default. Optional.',
    ],
  ]}
/>

**Description:**
Optional group name for organizing model specs in the UI selector. The `group` field provides flexible control over how model specs are organized:

- **If `group` matches an endpoint name** (e.g., `"openAI"`, `"groq"`): The model spec appears nested under that endpoint in the selector menu
- **If `group` is a custom name** (doesn't match any endpoint): Creates a separate collapsible section with that name. You can optionally use `groupIcon` to set a custom icon for this section (URL or built-in key like `"openAI"`)
- **If `group` is omitted**: The model spec appears as a standalone item at the top level

This feature is particularly useful when you want to add descriptions to models without losing the organizational structure of the selector menu.

---

### hideBadgeRow

<OptionTable
  options={[
    [
      'hideBadgeRow',
      'Boolean',
      'Hides the tool badge row for this model spec in the chat composer.',
      'hideBadgeRow: true',
    ],
  ]}
/>

**Default:** `false`

Use this when a curated model spec should not show the row of tool/capability badges beneath the composer.

**Example:**

```yaml filename="modelSpecs / hideBadgeRow"
modelSpecs:
  list:
    - name: 'general-assistant'
      label: 'General Assistant'
      hideBadgeRow: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

**Example:**

```yaml filename="modelSpecs with group field examples"
modelSpecs:
  list:
    # Example 1: Nested under an endpoint
    # When group matches an endpoint name, the spec appears under that endpoint
    - name: 'gpt-4o-optimized'
      label: 'GPT-4 Optimized'
      description: 'Most capable GPT-4 model with multimodal support'
      group: 'openAI' # Appears nested under the OpenAI endpoint
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'

    # Example 2: Custom group section with icon
    # When group is a custom name, it creates a separate collapsible section
    - name: 'coding-assistant'
      label: 'Coding Assistant'
      description: 'Specialized for coding tasks'
      group: 'My Assistants'
      groupIcon: 'https://example.com/icons/assistants.png' # Custom icon for the group
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'

    # Multiple specs with the same group name are grouped together
    - name: 'writing-assistant'
      label: 'Writing Assistant'
      description: 'Specialized for creative writing'
      group: 'My Assistants' # Grouped with coding-assistant, uses its icon
      preset:
        endpoint: 'anthropic'
        model: 'claude-sonnet-4'

    # Example 3: Custom group using built-in icon
    - name: 'fast-model'
      label: 'Fast Model'
      group: 'Fast Models'
      groupIcon: 'groq' # Uses built-in Groq icon
      preset:
        endpoint: 'groq'
        model: 'llama3-8b-8192'

    # Example 4: Standalone (no group)
    # When group is omitted, the spec appears at the top level
    - name: 'general-assistant'
      label: 'General Assistant'
      description: 'General purpose assistant'
      # No group field - appears as standalone item at top level
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### showIconInMenu

<OptionTable
  options={[
    [
      'showIconInMenu',
      'Boolean',
      "Controls whether the model's icon appears in the header dropdown menu.",
      '',
    ],
  ]}
/>

**Description:**  
Controls whether the model's icon appears in the header dropdown menu. Defaults to `true`.

---

### showIconInHeader

<OptionTable
  options={[
    [
      'showIconInHeader',
      'Boolean',
      "Controls whether the model's icon appears in the header dropdown button, left of its name.",
      '',
    ],
  ]}
/>

**Description:**  
Controls whether the model's icon appears in the header dropdown button, left of its name. Defaults to `true`.

---

### authType

<OptionTable
  options={[
    [
      'authType',
      'String',
      'Authentication type required for the model spec.',
      'Optional. Possible values: "override_auth", "user_provided", "system_defined"',
    ],
  ]}
/>

**Description:**  
Authentication type required for the model spec. Determines whether authentication is overridden, provided by the user, or defined by the system.

---

### webSearch

<OptionTable
  options={[
    [
      'webSearch',
      'Boolean',
      'Enables web search capability for this model spec.',
      'When true, the model can perform web searches.',
    ],
  ]}
/>

**Description:**  
Enables web search capability for this model spec. When set to `true`, the model can perform web searches to retrieve current information.

**Example:**

```yaml filename="modelSpecs / webSearch"
modelSpecs:
  list:
    - name: 'research-assistant'
      label: 'Research Assistant'
      webSearch: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### fileSearch

<OptionTable
  options={[
    [
      'fileSearch',
      'Boolean',
      'Enables file search capability for this model spec.',
      'When true, the model can search through uploaded files.',
    ],
  ]}
/>

**Description:**  
Enables file search capability for this model spec. When set to `true`, the model can search through and reference uploaded files.

**Example:**

```yaml filename="modelSpecs / fileSearch"
modelSpecs:
  list:
    - name: 'document-analyst'
      label: 'Document Analyst'
      fileSearch: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### executeCode

<OptionTable
  options={[
    [
      'executeCode',
      'Boolean',
      'Enables code execution capability for this model spec.',
      'When true, the model can execute code.',
    ],
  ]}
/>

**Description:**  
Enables code execution capability for this model spec. When set to `true`, the model can execute code in a sandboxed environment.

**Example:**

```yaml filename="modelSpecs / executeCode"
modelSpecs:
  list:
    - name: 'code-assistant'
      label: 'Code Assistant'
      executeCode: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### memory

<OptionTable
  options={[
    [
      'memory',
      'Boolean',
      'Equips the model spec\'s ephemeral agent with memory tools.',
      'false',
    ],
  ]}
/>

Requires configured [User Memory](/docs/features/memory), the endpoint-level `memory` capability, and user memory permissions. When enabled, the model can save or delete structured memories when the user explicitly requests it.

```yaml filename="modelSpecs / memory"
modelSpecs:
  list:
    - name: 'memory-assistant'
      memory: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### askUserQuestion

<OptionTable
  options={[
    [
      'askUserQuestion',
      'Boolean',
      'Lets the model spec pause to ask up to four related questions and resume with the answers.',
      'false',
    ],
  ]}
/>

Requires the endpoint-level `ask_user_question` capability. This is supported in Agent chat runs that provide LibreChat's durable pause/resume flow.

```yaml filename="modelSpecs / askUserQuestion"
modelSpecs:
  list:
    - name: 'interactive-assistant'
      askUserQuestion: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### runInBackground

<OptionTable
  options={[
    [
      'runInBackground',
      'Boolean or Array of Strings',
      'Controls which eligible tools attached to the model spec may run as background tasks.',
      'Unset (background-native code defaults apply)',
    ],
  ]}
/>

Requires the opt-in endpoint-level `run_in_background` capability. With that capability and `executeCode: true`, Code Interpreter execution and shell calls are background-eligible by default. Set `runInBackground: false` or `[]` to opt them out, `true` to enable every eligible tool attached to the spec, or an array of resolved tool IDs to enable only those tools. Selecting `execute_code` or `bash_tool` selects both native code tools; a list that omits them opts them out. Unsupported or misspelled selections are logged.

The model can start an eligible tool and continue working. For saved Agents, supported content-only completions are delivered automatically by default; `check_background_task` remains available for status, control, live artifacts, and recovery. Completed code output and generated files are attached to the original code call. Ordinary execution is process-local and does not survive the loss of its worker process, but a persisted terminal result may be delivered from another replica. Administrators can restore poll-only behavior with [`endpoints.agents.backgroundTasks.completionWakeups: false`](/docs/configuration/librechat_yaml/object_structure/agents#backgroundtasks).

```yaml filename="modelSpecs / runInBackground"
modelSpecs:
  list:
    - name: 'operations-assistant'
      mcpServers: ['operations']
      executeCode: true
      runInBackground:
        - 'execute_code'
        - 'slow_report_mcp_operations'
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### describeIntent

<OptionTable
  options={[
    [
      'describeIntent',
      'Boolean or Array of Strings',
      'Controls which eligible tools stream a model-written intent as their live tool-call label.',
      'Unset (native intent defaults apply)',
    ],
  ]}
/>

Requires the opt-in endpoint-level `tool_intents` capability. Native tools such as web search, file authoring, memory, and supported Code Interpreter tools use intent labels by default while the capability is enabled. Set `describeIntent: true` to enable every eligible tool, or provide resolved tool IDs to enable only those tools and disable labels for everything else. An empty array disables all intent labels for the spec. Omitting the field or setting it to `false` adds no broader selection, so native defaults still apply.

Intent labels are included in tool schemas and therefore add schema tokens to model requests. LibreChat logs selections that are misspelled, unavailable to the spec, or ineligible.

```yaml filename="modelSpecs / describeIntent"
modelSpecs:
  list:
    - name: 'research-assistant'
      webSearch: true
      mcpServers: ['research']
      describeIntent:
        - 'web_search'
        - 'search_mcp_research'
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o-mini'
```

---

### mcpServers

<OptionTable
  options={[
    [
      'mcpServers',
      'Array of Strings',
      'List of Model Context Protocol (MCP) server names to enable for this model spec.',
      'Each string should match a configured MCP server name.',
    ],
  ]}
/>

**Description:**  
List of Model Context Protocol (MCP) server names to enable for this model spec. MCP servers extend the model's capabilities with custom tools and resources.

**Example:**

```yaml filename="modelSpecs / mcpServers"
modelSpecs:
  list:
    - name: 'enhanced-assistant'
      label: 'Enhanced Assistant'
      mcpServers:
        - 'filesystem'
        - 'sequential-thinking'
        - 'fetch'
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### skills

<OptionTable
  options={[
    [
      'skills',
      'Boolean or Array of Strings',
      "Controls Skills for this model spec. Use true for the user's active accessible catalog, false to force Skills off, or an array of Skill names as a strict allowlist.",
      'skills: ["brand-guidelines", "code-review"]',
    ],
  ]}
/>

**Description:**

Controls Skills for this model spec when the Agents endpoint Skills capability is available.

- `true`: enables the user's active accessible Skill catalog.
- `false`: disables Skills for this spec.
- Array of Skill names: narrows catalog, manual invocation, and always-apply resolution to the named Skills.

**Example:**

```yaml filename="modelSpecs / skills"
modelSpecs:
  list:
    - name: 'brand-assistant'
      label: 'Brand Assistant'
      skills:
        - 'brand-guidelines'
        - 'approved-claims'
      preset:
        endpoint: 'agents'
        model: 'gpt-4o'
```

---

### subagents

<OptionTable
  options={[
    [
      'subagents.enabled',
      'Boolean',
      'Enables the Subagents capability for ephemeral agents created from this model spec.',
      'enabled: true',
    ],
    [
      'subagents.allowSelf',
      'Boolean',
      'Allows the ephemeral agent to spawn an isolated copy of itself for focused work.',
      'allowSelf: true',
    ],
    [
      'subagents.agent_ids',
      'Array of Strings',
      'Private server-side allowlist of additional agent IDs this model spec may spawn.',
      'agent_ids: []',
    ],
  ]}
/>

**Description:**

Controls Subagents for ephemeral agents created from this model spec. Use this when you want a curated model spec to expose delegation behavior without requiring users to create or select a persisted parent agent.

- `enabled`: adds the subagent spawn tool for this model spec.
- `allowSelf`: lets the ephemeral agent spawn a fresh isolated copy of itself.
- `agent_ids`: allows specific persisted agents as additional subagents. This list is capped by the effective subagents limit, which defaults to 10 and is configurable with [`endpoints.agents.maxSubagents`](/docs/configuration/librechat_yaml/object_structure/agents#maxsubagents). The list remains server-side; startup config sent to clients only includes public `enabled` and `allowSelf` flags.

When model specs are enforced, the model spec's `subagents` settings are authoritative over request payload values.

**Example:**

```yaml filename="modelSpecs / subagents"
modelSpecs:
  list:
    - name: 'research-assistant'
      label: 'Research Assistant'
      subagents:
        enabled: true
        allowSelf: true
        agent_ids: []
      preset:
        endpoint: 'agents'
        model: 'gpt-4o'
```

---

### artifacts

<OptionTable
  options={[
    [
      'artifacts',
      'String | Boolean',
      'Enables the Artifacts capability for this model spec and optionally sets the artifact mode.',
      'Set to `true` to enable with the default mode, `false` or omit to disable, or a specific mode string (e.g., `"default"`) to enable with that mode.',
    ],
  ]}
/>

**Description:**  
Enables the Artifacts capability for this model spec, allowing the model to generate and display interactive artifacts such as React components, HTML, and Mermaid diagrams. When set to `true`, the default artifact mode is used. You can also specify a mode string directly.

**Example:**

```yaml filename="modelSpecs / artifacts"
modelSpecs:
  list:
    - name: 'artifact-assistant'
      label: 'Artifact Assistant'
      artifacts: true
      preset:
        endpoint: 'openAI'
        model: 'gpt-4o'
```

---

### preset

<OptionTable
  options={[
    [
      'preset',
      'Object',
      'Detailed preset configurations that define the behavior and capabilities of the model.',
      'See "Preset Object Structure" below.',
    ],
  ]}
/>

**Description:**  
Detailed preset configurations that define the behavior and capabilities of the model (see Preset Object Structure below).

---

## Preset Fields

The `preset` field for a `modelSpecs.list` item is made up of a comprehensive configuration blueprint for AI models within the system. It is designed to specify the operational settings of AI models, tailoring their behavior, outputs, and interactions with other system components and endpoints.

### System Options

#### endpoint

**Required**

**Accepted Values:**

- `openAI`
- `azureOpenAI`
- `google`
- `anthropic`
- `assistants`
- `azureAssistants`
- `bedrock`
- `agents`

**Note:** If you are using a custom endpoint, the `endpoint` value must match the defined [custom endpoint name](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#name) exactly.

<OptionTable
  options={[
    [
      'endpoint',
      'Enum (EModelEndpoint) or String (nullable)',
      'Specifies the endpoint the model communicates with to execute operations. This setting determines the external or internal service that the model interfaces with.',
      '',
    ],
  ]}
/>

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / endpoint"
preset:
  endpoint: 'openAI'
```

---

#### modelLabel

<OptionTable
  options={[
    [
      'modelLabel',
      'String (nullable)',
      'The label used to identify the model in user interfaces or logs. It provides a human-readable name for the model, which is displayed in the UI, as well as made aware to the AI.',
      'None',
    ],
  ]}
/>

**Default:** `None`

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / modelLabel"
preset:
  modelLabel: 'Customer Support Bot'
```

---

#### greeting

<OptionTable
  options={[
    [
      'greeting',
      'String',
      'A predefined message that is visible in the UI before a new chat is started. This is a good way to provide instructions to the user, or to make the interface seem more friendly and accessible.',
      '',
    ],
  ]}
/>

**Default:** `None`

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / greeting"
preset:
  greeting: 'This assistant creates meeting notes based on transcripts of Teams recordings. To start, simply paste the transcript into the chat box.'
```

---

#### promptPrefix

<OptionTable
  options={[
    [
      'promptPrefix',
      'String (nullable)',
      'A static text prepended to every prompt sent to the model, setting a consistent context for responses.',
      'When using "assistants" as the endpoint, this becomes the OpenAI field `additional_instructions`.',
    ],
  ]}
/>

**Default:** `None`

**Example 1:**

```yaml filename="modelSpecs / list / {spec_item} / preset / promptPrefix"
preset:
  promptPrefix: 'As a financial advisor, ...'
```

**Example 2:**

```yaml filename="modelSpecs / list / {spec_item} / preset / promptPrefix"
preset:
  promptPrefix: |
    Based on the transcript, create coherent meeting minutes for a business meeting. Include the following sections:
    - Date and Attendees
    - Agenda
    - Minutes
    - Action Items

    Focus on what items were discussed and/or resolved. List any open action items.
    The format should be a bulleted list of high level topics in chronological order, and then one or more concise sentences explaining the details.
    Each high level topic should have at least two sub topics listed, but add as many as necessary to support the high level topic. 

    - Do not start items with the same opening words.

    Take a deep breath and be sure to think step by step.
```

---

#### resendFiles

<OptionTable
  options={[
    [
      'resendFiles',
      'Boolean',
      'Indicates whether files should be resent in scenarios where persistent sessions are not maintained.',
      '',
    ],
  ]}
/>

**Default:** `true`

Google model settings now expose this control in the shared schema, so administrators can explicitly choose whether files are resent on later turns for Google and Vertex AI model specs.

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / resendFiles"
preset:
  resendFiles: true
```

---

#### imageDetail

**Accepted Values:**

- low
- auto
- high

<OptionTable
  options={[
    [
      'imageDetail',
      'Enum (eImageDetailSchema)',
      'Specifies the level of detail required in image analysis tasks, applicable to models with vision capabilities (OpenAI spec).',
      '',
    ],
  ]}
/>

**Default:** `"auto"`

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / imageDetail"
preset:
  imageDetail: 'high'
```

---

#### maxContextTokens

<OptionTable
  options={[
    [
      'maxContextTokens',
      'Number',
      'The maximum number of context tokens to provide to the model.',
      'Useful if you want to limit the maximum context for this preset.',
    ],
  ]}
/>

For Google model settings, the Agent Builder accepts values from `10` to `2,000,000` and uses increments of `1,000`.

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / maxContextTokens"
preset:
  maxContextTokens: 4096
```

---

### Agent Options

Note that these options are only applicable when using the `agents` endpoint.

You should exclude any model options and defer to the agent's configuration as defined in the UI.

<Callout type="info" title="Agent Access Filtering (v0.8.0+)">
As of v0.8.0, LibreChat uses an ACL (Access Control List) based permissions system for agents. When model specs are configured to use agents, any agents that the user doesn't have access to will be automatically filtered out, even if they are configured in the model spec. This ensures users only see and can use agents they have proper permissions for.

The server also resolves the effective model spec and authorizes its persistent `agent_id` before loading or running that Agent. A caller cannot use request fields or a preset mismatch to substitute a different Agent after this access check.

For more information about the ACL permissions system, see the [Agents documentation](/docs/features/agents#migration-required-v080-rc3).

</Callout>

---

#### agent_id

<OptionTable options={[['agent_id', 'String', 'Identification of an assistant.', '']]} />

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / agent_id"
preset:
  agent_id: 'agent_someUniqueId'
```

---

### Assistant Options

Note that these options are only applicable when using the `assistants` or `azureAssistants` endpoint.

Similar to [Agents](#agent-options), you should exclude any model options and defer to the assistant's configuration.

---

#### assistant_id

<OptionTable options={[['assistant_id', 'String', 'Identification of an assistant.', '']]} />

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / assistant_id"
preset:
  assistant_id: 'asst_someUniqueId'
```

---

#### instructions

**Note:** this is distinct from [`promptPrefix`](#promptPrefix), as this overrides existing assistant instructions for current runs.

Only use this if you want to override the assistant's core instructions.

Use [`promptPrefix`](#promptPrefix) for `additional_instructions`.

More information:

- https://platform.openai.com/docs/api-reference/models#runs-createrun-instructions
- https://platform.openai.com/docs/api-reference/runs/createRun#runs-createrun-additional_instructions

<OptionTable
  options={[['instructions', 'String', "Overrides the assistant's default instructions.", '']]}
/>

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / instructions"
preset:
  instructions: 'Please handle customer queries regarding order status.'
```

---

#### append_current_datetime

Adds the current date and time to `additional_instructions` for each run. Does not overwrite `promptPrefix`, but adds to it.

<OptionTable
  options={[
    [
      'append_current_datetime',
      'Boolean',
      'Adds the current date and time to `additional_instructions` as defined by `promptPrefix`',
      '',
    ],
  ]}
/>

**Example:**

```yaml filename="modelSpecs / list / {spec_item} / preset / append_current_datetime"
preset:
  append_current_datetime: true
```

---

### Model Options

> **Note:** Each parameter below includes a note on which endpoints support it.  
> **OpenAI / AzureOpenAI / Custom** typically support `temperature`, `presence_penalty`, `frequency_penalty`, `stop`, `top_p`, `max_tokens`.  
> **Google / Anthropic** typically support `topP`, `topK`, `maxOutputTokens`; Google also supports `url_context` on supported Gemini text models.
> **Anthropic / OpenRouter / Bedrock (Anthropic and Nova models)** support `promptCache` and `promptCacheTtl`.
> **Bedrock** supports `region`, `maxTokens`, and a few others.

#### model

> **Supported by:** All endpoints (except `agents`)

<OptionTable
  options={[
    [
      'model',
      'String (nullable)',
      'The model name to use for the preset, matching a configured model under the chosen endpoint.',
      'None',
    ],
  ]}
/>

**Default:** `None`

**Example:**

```yaml
preset:
  model: 'gpt-4-turbo'
```

---

#### temperature

> **Supported by:** `openAI`, `azureOpenAI`, `google` (as `temperature`), `anthropic` (as `temperature`), and custom (OpenAI-like)

<OptionTable
  options={[
    [
      'temperature',
      'Number',
      'Controls how deterministic or “creative” the model responses are.',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  temperature: 0.7
```

---

#### presence_penalty

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)  
> _Not typically used by Google/Anthropic/Bedrock_

<OptionTable
  options={[
    [
      'presence_penalty',
      'Number',
      'Penalty for repetitive tokens, encouraging exploration of new topics.',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  presence_penalty: 0.3
```

---

#### frequency_penalty

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)  
> _Not typically used by Google/Anthropic/Bedrock_

<OptionTable
  options={[
    [
      'frequency_penalty',
      'Number',
      'Penalty for repeated tokens, reducing redundancy in responses.',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  frequency_penalty: 0.5
```

---

#### stop

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)  
> _Not typically used by Google/Anthropic/Bedrock_

<OptionTable
  options={[
    [
      'stop',
      'Array of Strings',
      'Stop tokens for the model, instructing it to end its response if encountered.',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  stop:
    - 'END'
    - 'STOP'
```

---

#### top_p

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)  
> **Google/Anthropic** often use `topP` (capital “P”) instead of `top_p`.

<OptionTable
  options={[
    [
      'top_p',
      'Number',
      'Nucleus sampling parameter (0-1), controlling the randomness of tokens.',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  top_p: 0.9
```

---

#### topP

> **Supported by:** `google` & `anthropic`  
> (similar purpose to `top_p`, but named differently in those APIs)

<OptionTable
  options={[['topP', 'Number', 'Nucleus sampling parameter for Google/Anthropic endpoints.', '']]}
/>

**Example:**

```yaml
preset:
  topP: 0.8
```

---

#### topK

> **Supported by:** `google` & `anthropic`  
> (k-sampling limit on the next token distribution)

<OptionTable
  options={[['topK', 'Number', 'Limits the next token selection to the top K tokens.', '']]}
/>

**Example:**

```yaml
preset:
  topK: 40
```

---

#### max_tokens

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)  
> _For Google/Anthropic, use `maxOutputTokens` or `maxTokens` (depending on the endpoint)._

<OptionTable
  options={[['max_tokens', 'Number', 'The maximum number of tokens in the model response.', '']]}
/>

**Example:**

```yaml
preset:
  max_tokens: 4096
```

---

#### maxOutputTokens

> **Supported by:** `google`, `anthropic`  
> _Equivalent to `max_tokens` for these providers._

<OptionTable
  options={[
    [
      'maxOutputTokens',
      'Number',
      'The maximum number of tokens in the response (Google/Anthropic).',
      '',
    ],
  ]}
/>

**Example:**

```yaml
preset:
  maxOutputTokens: 2048
```

---

#### promptCache

> **Supported by:** `anthropic`, OpenRouter custom endpoints, `bedrock` (Anthropic and Nova models)
> (Toggle provider prompt caching)

<OptionTable
  options={[
    ['promptCache', 'Boolean', 'Enables or disables provider prompt caching.', ''],
  ]}
/>

**Default:** `true`

**Example:**

```yaml
preset:
  promptCache: true
```

**Note:** For Bedrock endpoints, prompt caching is automatically enabled for Claude and Nova models. Set `promptCache: false` to explicitly disable it.

---

#### promptCacheTtl

> **Supported by:** `anthropic`, OpenRouter custom endpoints, `bedrock` (Anthropic and Nova models)
> (Sets the prompt-cache lifetime when prompt caching is enabled)

<OptionTable
  options={[
    [
      'promptCacheTtl',
      'Enum',
      'Sets the prompt-cache lifetime. Supported values are `5m` and `1h`.',
      'Provider or SDK default',
    ],
  ]}
/>

**Accepted Values:**

- `5m`
- `1h`

**Example:**

```yaml
preset:
  promptCache: true
  promptCacheTtl: '1h'
```

**Note:** `promptCacheTtl` is ignored when prompt caching is disabled. When omitted, the provider integration uses its default prompt-cache lifetime.

---

#### reasoning_effort

**Accepted Values:**

- `""` (empty string — unset, uses API default)
- `"none"`
- `"minimal"`
- `"low"`
- `"medium"`
- `"high"`
- `"xhigh"` (extra high)
- `"max"` (maximum)

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like), `bedrock` (ZAI, MoonshotAI models)

<OptionTable
  options={[
    [
      'reasoning_effort',
      'String',
      'Controls the reasoning effort level for the model. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning. Supported OpenAI models may expose `xhigh` and `max`; for Bedrock, accepted values are `low`, `medium`, `high`.',
      '',
    ],
  ]}
/>

**Default:** `""` (unset)

**Example:**

```yaml
preset:
  reasoning_effort: 'low'
```

---

#### reasoning_summary

**Accepted Values:**

- `""` (empty string — disables reasoning summaries)
- `"auto"`
- `"concise"`
- `"detailed"`

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)

<OptionTable
  options={[
    ['reasoning_summary', 'String', 'Sets reasoning summary preferences for the model.', ''],
  ]}
/>

**Default:** `""` (disabled)

**Example:**

```yaml
preset:
  reasoning_summary: 'detailed'
```

---

#### reasoning_mode

**Accepted Values:**

- `""` (empty string - unset)
- `"standard"`
- `"pro"`

> **Supported by:** OpenAI Responses API models that expose reasoning mode, including GPT-5.6

<OptionTable
  options={[
    ['reasoning_mode', 'String', 'Sets the OpenAI Responses API reasoning mode.', ''],
  ]}
/>

```yaml
preset:
  reasoning_effort: 'high'
  reasoning_mode: 'pro'
```

---

#### reasoning_context

**Accepted Values:**

- `""` (empty string - unset)
- `"auto"`
- `"current_turn"`
- `"all_turns"`

> **Supported by:** OpenAI Responses API models that expose reasoning context, including GPT-5.6

<OptionTable
  options={[
    [
      'reasoning_context',
      'String',
      'Controls how much conversational context is available to model reasoning.',
      '',
    ],
  ]}
/>

```yaml
preset:
  reasoning_effort: 'high'
  reasoning_context: 'all_turns'
```

For GPT-5.6, LibreChat defaults reasoning requests to the Responses API unless `useResponsesApi: false` is set explicitly.

---

#### useResponsesApi

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)

<OptionTable
  options={[
    ['useResponsesApi', 'Boolean', 'Enables or disables the responses API for the model.', ''],
  ]}
/>

**Default:** `false`

**Example:**

```yaml
preset:
  useResponsesApi: true
```

---

#### verbosity

**Accepted Values:**

- `""` (empty string — unset, uses API default)
- `"low"`
- `"medium"`
- `"high"`

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)

<OptionTable
  options={[['verbosity', 'String', 'Controls the verbosity level of model responses.', '']]}
/>

**Default:** `""` (unset)

**Example:**

```yaml
preset:
  verbosity: 'low'
```

---

#### web_search

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like), `google`, `anthropic`

<OptionTable
  options={[
    ['web_search', 'Boolean', 'Enables or disables web search functionality for the model.', ''],
  ]}
/>

**Default:** `false`

**Note:** For Google endpoints, this parameter appears as `Grounding with Google Search` in the actual panel but controls `web_search` in the implementation.

**Example:**

```yaml
preset:
  web_search: true
```

---

#### url_context

> **Supported by:** `google` on supported Gemini text models, including Gemini 2.5+ and Gemini 3.x

<OptionTable
  options={[
    [
      'url_context',
      'Boolean',
      'Enables Google URL Context so the model can read URLs included in the user message. YouTube links are converted to native video-understanding inputs when possible.',
      '',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml
preset:
  url_context: true
```

---

#### disableStreaming

> **Supported by:** `openAI`, `azureOpenAI`, custom (OpenAI-like)

<OptionTable
  options={[['disableStreaming', 'Boolean', 'Disables streaming responses from the model.', '']]}
/>

**Default:** `false`

**Example:**

```yaml
preset:
  disableStreaming: true
```

---

#### thinkingBudget

> **Supported by:** `google`, `anthropic`, `bedrock` (Anthropic models)

<OptionTable
  options={[
    [
      'thinkingBudget',
      'Number or String',
      'Controls the number of thinking tokens the model can use for internal reasoning. Larger budgets can improve response quality for complex problems.',
      '',
    ],
  ]}
/>

**Default:** `"Auto (-1)"` (Google), `2000` (Anthropic, Bedrock (Anthropic models))

For Gemini 2.5 models, the Google setting is model-aware: Pro accepts `128`-`32768`, Flash accepts `0`-`24576`, and Flash-Lite accepts `512`-`24576`. `-1` remains the automatic setting. Gemini 3+ models use [`thinkingLevel`](#thinkinglevel) instead of a numeric budget.

**Example:**

```yaml
preset:
  thinkingBudget: '2000'
```

---

#### thinkingLevel

> **Supported by:** `google` (Gemini 3+ models)

<OptionTable
  options={[
    [
      'thinkingLevel',
      'String',
      'Controls the thinking effort level for Gemini 3+ models. Gemini 2.5 models use `thinkingBudget` instead.',
      '',
    ],
  ]}
/>

**Accepted Values:**

- `""` (unset/auto)
- `"minimal"`
- `"low"`
- `"medium"`
- `"high"`

**Default:** `""` (unset — model decides)

**Example:**

```yaml
preset:
  thinkingLevel: 'medium'
```

---

#### effort

> **Supported by:** `anthropic`, `bedrock` (Anthropic models)

<OptionTable
  options={[
    [
      'effort',
      'String',
      'Controls the Adaptive Thinking effort level for supported Anthropic models (e.g., Claude Opus 4.6+ and Claude Fable/Mythos-class models). Higher effort levels allocate more thinking tokens for complex problems.',
      '',
    ],
  ]}
/>

**Options:** `""` (unset/auto), `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`

**Default:** `""` (unset — model decides)

**Example:**

```yaml
preset:
  effort: 'high'
```

---

#### thinkingDisplay

> **Supported by:** `anthropic`, `bedrock` (Anthropic models)

<OptionTable
  options={[
    [
      'thinkingDisplay',
      'String',
      'Controls whether reasoning content is returned in model responses. Claude Opus 4.7+ and Claude Fable/Mythos-class models omit thinking content by default; this setting lets you opt in to reasoning summaries or explicitly suppress them.',
      '',
    ],
  ]}
/>

**Options:** `"auto"` (default), `"summarized"`, `"omitted"`

- `"auto"` — LibreChat decides: opts in to `"summarized"` for models that omit thinking by default (Opus 4.7+ and Fable/Mythos-class), leaves the field off for older models
- `"summarized"` — always request a post-hoc summary of the reasoning
- `"omitted"` — always suppress reasoning content (slightly lower latency)

**Default:** `"auto"`

**Example:**

```yaml
preset:
  thinkingDisplay: 'summarized'
```

---

#### thinking

> **Supported by:** `google`, `anthropic`, `bedrock` (Anthropic models)

<OptionTable
  options={[
    [
      'thinking',
      'Boolean',
      'Indicates whether the model should spend time thinking before generating a response.',
      '',
    ],
  ]}
/>

**Default:** `true`

**Example:**

```yaml
preset:
  thinking: true
```

---

#### region

> **Supported by:** `bedrock`  
> (Used to specify an AWS region for Amazon Bedrock)

<OptionTable options={[['region', 'String', 'AWS region for Amazon Bedrock endpoints.', '']]} />

**Example:**

```yaml
preset:
  region: 'us-east-1'
```

---

#### maxTokens

> **Supported by:** `bedrock`  
> (Used in place of `max_tokens`)

<OptionTable
  options={[['maxTokens', 'Number', 'Maximum output tokens for Amazon Bedrock endpoints.', '']]}
/>

**Example:**

```yaml
preset:
  maxTokens: 1024
```


# Model Config Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/model_config)

Each item under `models` is part of a list of records, either a boolean value or Object:

**When specifying a model as an object:**

An object allows for detailed configuration of the model, including its `deploymentName` and/or `version`. This mode is used for more granular control over the models, especially when working with multiple versions or deployments under one instance or resource group.

**Example**:
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / models / {model_item=Object}"
models:
  gpt-4-vision-preview:
    deploymentName: "gpt-4-vision-preview"
    version: "2024-02-15-preview"
```

**Notes:**
- **Deployment Names** and **Versions** are critical for ensuring that the correct model is used.
    - Double-check these values for accuracy to prevent unexpected behavior.

### deploymentName

**Key:**
<OptionTable
  options={[
    ['deploymentName', 'String', 'The name of the deployment for the model. Identifies the deployment of the model within Azure.', 'This does not have to be the matching OpenAI model name as is convention, but must match the actual name of your deployment on Azure.'],
  ]}
/>

**Required:** yes

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / models / {model_item=Object} / deploymentName"
deploymentName: "gpt-4-vision-preview"
```

## version

**Key:**
<OptionTable
  options={[
    ['version', 'String', 'Specifies the version of the model. Defines the version of the model to be used.', ''],
  ]}
/>

**Required:** yes

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / models / {model_item=Object} / version"
version: "2024-02-15-preview"
```

**Enabling a Model with Default Group Configuration**

**Key:**
<OptionTable
  options={[
    ['models', 'Boolean', 'Enables a model with default group configuration.', 'When a model is enabled (`true`) without using an object, it uses the group\'s configuration values for deployment name and version.'],
  ]}
/>

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / models"
models:
  gpt-4-turbo: true
```

# Default Parameters (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/default_params)

**Note** 
- The purpose of this part of the documentation is to help understand what `addParams` and `dropParams` do. You **CANNOT** globally configure the parameters and their values that LibeChat sends by default, it can only be configured within a single endpoint.

Custom endpoints share logic with the OpenAI endpoint, and thus have default parameters tailored to the OpenAI API.

```yaml filename="Default Parameters"
{
  "model": "your-selected-model",
  "user": "LibreChat_User_ID",
  "stream": true,
  "messages": [
    {
      "role": "user",
      "content": "hi how are you",
    },
  ],
}
```

### Breakdown
- `model`: The selected model from list of models.
- `user`: A unique identifier representing your end-user, which can help OpenAI to [monitor and detect abuse](https://platform.openai.com/docs/api-reference/chat/create#chat-create-user).
- `stream`: If set, partial message deltas will be sent, like in ChatGPT. Otherwise, generation will only be available when completed.
- `messages`: [OpenAI format for messages](https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages); the `name` field is added to messages with `system` and `assistant` roles when a custom name is specified via preset.

**Note:** The `max_tokens` field is not sent to use the maximum amount of tokens available, which is default OpenAI API behavior. Some alternate APIs require this field, or it may default to a very low value and your responses may appear cut off; in this case, you should add it to `addParams` field as shown in the [Custom Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/custom_endpoint).


# Custom Parameters (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/custom_params)

### Picking A Default Parameters Set

By default, when you specify a custom endpoint in `librechat.yaml` config file, it will use the default parameters of the OpenAI API. However, you can override these defaults by specifying the `customParams.defaultParamsEndpoint` field within the definition of your custom endpoint. For example, to use Google parameters for your custom endpoint:

```yaml filename="excerpt of librechat.yaml"
endpoints:
  custom:
    - name: 'Google Gemini'
      apiKey: ...
      baseURL: ...
      customParams:
        defaultParamsEndpoint: 'google'
```

Your "Google Gemini" endpoint will now display parameters for Google API when you create a new agent or preset.

#### Accepted Values

`defaultParamsEndpoint` selects which built-in parameter set the endpoint's panel renders. These are the values that resolve to one:

<OptionTable
  options={[
    ['custom', 'String', 'OpenAI parameter set. This is the schema default, and what an endpoint with no `provider` falls back to.', 'Default'],
    ['openAI', 'String', 'OpenAI parameter set.', ''],
    ['azureOpenAI', 'String', 'OpenAI parameter set.', ''],
    ['anthropic', 'String', 'Anthropic parameter set.', ''],
    ['google', 'String', 'Google parameter set.', ''],
    ['openrouter', 'String', 'OpenRouter parameter set. Lowercase, unlike the others.', ''],
  ]}
/>

Note the casing: `openAI` and `azureOpenAI` are camelCase, but `openrouter` is all lowercase.

<Callout type="info" title="A provider setting supplies this value for you">

If the custom endpoint sets `provider`, LibreChat fills `defaultParamsEndpoint` in from it, so the effective default is the provider's parameter set rather than `custom`. That substitution happens only when you leave the field out or leave it at `custom`; any other value you set explicitly wins. A `provider: 'anthropic'` endpoint therefore starts on the Anthropic parameter set without you writing `defaultParamsEndpoint` at all.

</Callout>

The values `assistants`, `azureAssistants`, `agents`, and `bedrock` are also recognized when LibreChat resolves conversation and preset schemas, but none of them maps to a parameter set a custom endpoint can render, so setting one leaves the panel empty.

<Callout type="warning" title="Unrecognized values fail silently">

The field is a free-form string, not a closed enum, so a value outside this list passes configuration validation. It simply matches no parameter set, and the endpoint's panel renders with **no parameters at all** rather than reporting an error. If your parameter panel is unexpectedly empty, check this value first.

</Callout>

### Overriding Parameter Definitions

On top of that, you can also fine tune the parameters provided for your custom endpoint. For example, the `temperature` parameter for google endpoint is a slide with range from 0.0 to 1.0, and default of 1.0, you can update the `librechat.yaml` file to override these values:

```yaml filename="excerpt of librechat.yaml"
endpoints:
  custom:
    - name: 'Google Gemini'
      apiKey: ...
      baseURL: ...
      customParams:
        defaultParamsEndpoint: 'google'
        paramDefinitions:
          - key: temperature
            range:
              min: 0
              max: 0.7
              step: 0.1
            default: 0.5
```

As a result, the `Temperature` slider will be limited to the range of `0.0` and `0.7` with step of `0.1`, and a default of `0.5`. The rest of the parameters will be set to their default values.

#### Sentinel ranges

Use `range.positiveMin` when `range.min` is a special sentinel value but regular values have a higher lower bound. For example, a thinking budget can use `-1` for automatic behavior while accepting explicit budgets only from `128` through `32768`:

```yaml filename="excerpt of librechat.yaml"
endpoints:
  custom:
    - name: 'My Gemini Gateway'
      apiKey: ...
      baseURL: ...
      customParams:
        paramDefinitions:
          - key: thinkingBudget
            type: number
            component: slider
            range:
              min: -1
              positiveMin: 128
              max: 32768
              step: 1
            default: -1
```

With `positiveMin`, the only accepted values are the sentinel `min` or values from `positiveMin` through `max`; values between them are not valid. `positiveMin` cannot exceed `max`, and the default must be either `min` or at least `positiveMin`.

### Setting Default Parameter Values

You can specify default values for parameters that will be automatically applied when making API requests. This is useful for setting baseline parameter values for your custom endpoint without requiring users to manually configure them each time.

The `default` field in `paramDefinitions` allows you to set default values that are applied when parameters are undefined. These defaults follow a priority order to ensure proper override behavior:

**Priority Order (lowest to highest):**

1. **Default values from `paramDefinitions`** - Applied first when parameter is undefined
2. **`addParams`** - Can override default values
3. **User-configured `modelOptions`** - Highest priority, overrides everything

```yaml filename="excerpt of librechat.yaml"
endpoints:
  custom:
    - name: 'My Custom LLM'
      apiKey: ...
      baseURL: ...
      customParams:
        defaultParamsEndpoint: 'openAI'
        paramDefinitions:
          - key: temperature
            default: 0.7
          - key: topP
            default: 0.9
          - key: maxTokens
            default: 2000
```

In this example:

- If a user doesn't specify `temperature`, it defaults to `0.7`
- If a user explicitly sets `temperature` to `0.5`, their value (`0.5`) takes precedence
- The `addParams` field (if configured) can override these defaults
- User selections in the UI always have the highest priority

### Anthropic

There are two Anthropic-related custom endpoint modes:

- `provider: 'anthropic'` on the custom endpoint uses the native Anthropic `/v1/messages` client. Use this for Anthropic itself or gateways that speak the Anthropic Messages API.
- `customParams.defaultParamsEndpoint: 'anthropic'` keeps the custom endpoint on the OpenAI-compatible path while applying Anthropic-style parameter metadata and request adaptation.

When using `defaultParamsEndpoint: 'anthropic'`, the system provides special handling that goes beyond just displaying and using Anthropic parameter sets:

<Callout type="info">
**Anthropic API Compatibility**

Setting `defaultParamsEndpoint: 'anthropic'` adapts parameters, headers, and payload formatting for Anthropic-shaped requests on the custom endpoint path:

- Parameters are sent to your custom endpoint exactly as the Anthropic API expects
- This is essential for proxy services like LiteLLM that pass non-OpenAI-spec parameters directly to the underlying provider
- Anthropic-specific parameters like `thinking` are properly formatted
- The `messages` payload is formatted according to Anthropic's requirements (thinking blocks and prompt caching)
- Appropriate beta headers are automatically added based on the model as when using Anthropic directly

</Callout>

This is mainly necessary to properly format the `thinking` parameter, which is not OpenAI-compatible:

```json
{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 10000
  }
}
```

Additionally, the system automatically adds model-specific Anthropic beta headers such as:

- `anthropic-beta: prompt-caching-2024-07-31` for prompt caching support
- `anthropic-beta: context-1m-2025-08-07` for extended context models
- Model-specific feature flags based on the Claude model being used

For native Anthropic-compatible endpoints, prefer `provider: 'anthropic'` on the custom endpoint. It routes agents, summarization, token/context budgeting, and parameter defaults through the Anthropic provider path.

### Reasoning replay

Some OpenAI-compatible reasoning gateways require provider `reasoning_content` to be replayed on assistant tool-call turns. Use custom endpoint flags to opt in only for providers that need this behavior:

```yaml filename="endpoints / custom / customParams"
customParams:
  reasoningFormat: reasoning_object
  reasoningKey: reasoning_content
  includeReasoningContent: true
```

Set `includeReasoningHistory: true` only when the provider also requires LibreChat to reconstruct `reasoning_content` from persisted conversation history across later turns. This implies `includeReasoningContent`.

<Callout type="note">
**Implementation Status**

Currently, this automatic parameter and header handling is fully implemented for Anthropic-style custom endpoints. Similar behavior for other `defaultParamsEndpoint` values (e.g., `google`, `bedrock`) is planned for future updates.

</Callout>


# Shared Endpoint Settings (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings)

This page describes the shared configuration settings for all endpoints. The settings highlighted here are available to all configurations under the ["Endpoints"](/docs/configuration/librechat_yaml/object_structure/config#endpoints) field unless noted otherwise.

## Example Configuration

```yaml filename="Shared Endpoint Settings"
endpoints:
  # Individual endpoint configurations
  openAI:
    streamRate: 25
    titleModel: 'gpt-4o-mini'
    titleMethod: 'completion'
    titleTiming: 'immediate'
    titlePrompt: "Create a concise title for this conversation:\n\n{convo}"
    headers:
      X-Gateway-Metadata: '{"user_email":"{{LIBRECHAT_USER_EMAIL}}"}'

  azureOpenAI:
    streamRate: 35
    titleModel: 'grok-3'
    titleMethod: 'structured'
    titlePrompt: |
      Analyze this conversation and provide:
      1. A concise title in the detected language (5 words or less, no punctuation or quotation)
      2. Always provide a relevant emoji at the start of the title

      {convo}
    titleConvo: true

  anthropic:
    streamRate: 25
    titleModel: 'claude-3-5-haiku-20241022'
    titleMethod: 'completion'
    headers:
      X-Conversation-Id: '{{LIBRECHAT_BODY_CONVERSATIONID}}'

  bedrock:
    streamRate: 25
    titleModel: 'us.amazon.nova-lite-v1:0'
    titleEndpoint: 'anthropic'

  google:
    streamRate: 25
    titleModel: 'gemini-2.0-flash-lite'
    titlePromptTemplate: "Human: {input}\nAssistant: {output}"
    headers:
      X-Gateway-Metadata: '{"user_id":"{{LIBRECHAT_USER_ID}}"}'

  assistants:
    streamRate: 30

  azureAssistants:
    streamRate: 30

  agents:
    activityLabel: true
    activityEndpoint: 'openAI'
    activityModel: 'gpt-4.1-nano'
    activityMaxPerRun: 20
    activityCharLimit: 600
    activityPhaseLabel: true
    activityPhaseMaxPerRun: 5

  # Global configuration using 'all' - this applies shared settings across endpoints.
  # Most defined values override endpoint defaults; headers are merged and endpoint values win on collisions.
  all:
    headers:
      X-App: 'librechat'
    titleConvo: true
    titleModel: 'gpt-4.1-nano'
    titleTiming: 'immediate'
    titlePrompt: |
      Analyze this conversation and provide:
      1. The detected language of the conversation
      2. A concise title in the detected language (5 words or less, no punctuation or quotation)
      3. Always provide a relevant emoji at the start of the title
      {convo}
```

> **Important:** When using the `all` configuration, most shared properties you define apply across endpoints. In the example above, the `all` configuration would apply `titleConvo`, `titleModel`, and `titlePrompt` to all endpoints, while individual `streamRate` settings would be preserved since it's not defined in `all`. `headers` are merged separately: values from `endpoints.all.headers` apply globally, and endpoint-level headers win on key collisions. Activity settings resolve field by field in the same order, so an unrelated `all` setting does not hide an endpoint's activity configuration.

## streamRate

**Key:**

<OptionTable
  options={[
    [
      'streamRate',
      'Number',
      'The rate at which data is streamed from the endpoint. Useful for controlling the pace of streaming data.',
      'streamRate: 25',
    ],
  ]}
/>

**Default:** Provider-dependent. OpenAI, custom endpoints, Anthropic, Google, Bedrock, and Agents SDK-backed streams use adaptive smoothing with a 25 ms target. Legacy Assistants and Ollama handlers retain their per-provider-chunk behavior, with a 1 ms default.

Adaptive smoothing emits the first text token without delay, increases chunk size when a stream falls behind, and preserves tool-call and metadata ordering. Set `streamRate` to a non-negative millisecond target; `streamRate: 0` disables adaptive smoothing on the SDK-backed providers listed above.

An endpoint-specific value is preserved when `endpoints.all` exists without `streamRate`. Defining `endpoints.all.streamRate` overrides endpoint values, including an explicit `0`.

## titleConvo

**Key:**

<OptionTable
  options={[
    [
      'titleConvo',
      'Boolean',
      'Enables automatic conversation title generation for this endpoint.',
      'titleConvo: true',
    ],
  ]}
/>

**Default:** `false`

**Notes:**

- When enabled, titles will be generated automatically using the configured title settings
- Must be used in conjunction with `titleModel` or the endpoint must have a default model available

**Example:**

```yaml filename="titleConvo"
titleConvo: true
```

## titleTiming

**Key:**

<OptionTable
  options={[
    [
      'titleTiming',
      'String',
      'Controls when conversation titles are generated. Valid values: "immediate" or "final".',
      'titleTiming: "immediate"',
    ],
  ]}
/>

**Default:** `"immediate"`

**Available Values:**

- **`"immediate"`** - Generates the title as soon as the request starts, in parallel with the model response, using the user's first message. Titles usually appear within a second or two.
- **`"final"`** - Defers title generation until the full response completes. This preserves the legacy behavior.

**Example:**

```yaml filename="titleTiming"
endpoints:
  all:
    titleTiming: 'immediate'
```

## titleModel

**Key:**

<OptionTable
  options={[
    [
      'titleModel',
      'String',
      'Specifies the model to use for titles.',
      'Defaults to system default for the current endpoint if omitted. May cause issues if the system default model is not available. You can also dynamically use the current conversation model by setting it to "current_model".',
    ],
  ]}
/>

**Default:** System default for the current endpoint

## titleMethod

**Key:**

<OptionTable
  options={[
    [
      'titleMethod',
      'String',
      'Controls the method used for generating conversation titles.',
      'Valid values: "completion" (default), "structured", "functions" (legacy alias for "structured")',
    ],
  ]}
/>

**Default:** `"completion"`

**Available Methods:**

- **`"completion"`** - Uses standard completion API without tools/functions. Compatible with most LLMs.
- **`"structured"`** - Uses structured output for title generation. Requires provider/model support.
- **`"functions"`** - Legacy alias for "structured". Functionally identical.

**Example:**

```yaml filename="titleMethod"
titleMethod: 'completion'
```

## titlePrompt

**Key:**

<OptionTable
  options={[
    [
      'titlePrompt',
      'String',
      'Custom prompt for title generation. Must include {convo} placeholder.',
      'Allows full control over how titles are generated.',
    ],
  ]}
/>

**Default:**

```
Analyze this conversation and provide:
1. The detected language of the conversation
2. A concise title in the detected language (5 words or less, no punctuation or quotation)

{convo}
```

**Notes:**

- Must always include the `{convo}` placeholder
- The `{convo}` placeholder will be replaced with the formatted conversation
- Can be placed anywhere in the prompt

**Example:**

```yaml filename="titlePrompt"
titlePrompt: "Create a brief, descriptive title for the following conversation:\n\n{convo}\n\nTitle:"
```

## titlePromptTemplate

**Key:**

<OptionTable
  options={[
    [
      'titlePromptTemplate',
      'String',
      'Template for formatting the conversation content that replaces {convo} in titlePrompt.',
      'Must include {input} and {output} placeholders.',
    ],
  ]}
/>

**Default:** `"User: {input}\nAI: {output}"`

**Notes:**

- Must include both `{input}` and `{output}` placeholders
- `{input}` is replaced with the user's initial message
- `{output}` is replaced with the AI's response
- The formatted result replaces `{convo}` in the titlePrompt

**Example:**

```yaml filename="titlePromptTemplate"
titlePromptTemplate: "Human: {input}\n\nAssistant: {output}"
```

## titleEndpoint

**Key:**

<OptionTable
  options={[
    [
      'titleEndpoint',
      'String',
      'Specifies an alternative endpoint to use for title generation.',
      'Allows using a different, potentially cheaper model/endpoint for titles.',
    ],
  ]}
/>

**Default:** Uses the current conversation's endpoint

**Accepted Values:**

- `openAI`
- `azureOpenAI`
- `google`
- `anthropic`
- `bedrock`
- For custom endpoints: use the exact [custom endpoint name](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#name)

**Example:**

```yaml filename="titleEndpoint"
# Use Anthropic for titles even when chatting with OpenAI
endpoints:
  openAI:
    titleEndpoint: 'anthropic'
    # Will use anthropic's configuration for title generation
```

## Agent Activity Groups

Activity groups make long Agent runs easier to scan by collapsing each contiguous block of reasoning and tool calls under a generated one-line header. Header generation runs outside the main model-response path, but it is still a model call and its usage is recorded and billed.

With parent phase labels enabled, short progress text stays inside the active phase. A phase closes before substantial answer text so the result remains outside the collapsed summary, and a later block of reasoning or tool calls can begin another phase in the same run. LibreChat also rebases saved phase boundaries when malformed or omitted content is compacted, preserving their alignment after reload.

<OptionTable
  options={[
    [
      'activityLabel',
      'Boolean',
      'Enables generated activity-group headers for Agent runs using this endpoint.',
      'false',
    ],
    [
      'activityModel',
      'String',
      'Model used to generate headers. Falls back to titleModel, then the Agent run model. Use current_model to select the run model explicitly.',
      '',
    ],
    [
      'activityEndpoint',
      'String',
      'Endpoint whose credentials are used for header generation. Defaults to the Agent model provider.',
      '',
    ],
    [
      'activityPrompt',
      'String',
      'Overrides the prompt used to generate the one-line header.',
      '',
    ],
    [
      'activityMaxPerRun',
      'Positive integer',
      'Maximum generated activity headers per response, limiting additional model calls.',
      '20',
    ],
    [
      'activityCharLimit',
      'Positive integer',
      'Maximum characters from each tool input or output entry included in the header prompt.',
      '600',
    ],
    [
      'activityPhaseLabel',
      'Boolean',
      'Generates a collapsed parent summary for each run phase containing at least two activities.',
      'false',
    ],
    [
      'activityPhaseModel',
      'String',
      'Model used for parent phase summaries. Falls back to activityModel, titleModel, then the Agent run model.',
      '',
    ],
    [
      'activityPhaseEndpoint',
      'String',
      'Endpoint whose credentials are used for phase summaries. Falls back to activityEndpoint, then the Agent provider.',
      '',
    ],
    [
      'activityPhasePrompt',
      'String',
      'Overrides the prompt used to generate parent phase summaries.',
      '',
    ],
    [
      'activityPhaseMaxPerRun',
      'Positive integer',
      'Maximum generated parent phase summaries per response.',
      '5',
    ],
  ]}
/>

```yaml filename="endpoints / agents / activity groups"
endpoints:
  agents:
    activityLabel: true
    activityEndpoint: 'openAI'
    activityModel: 'gpt-4.1-nano'
    activityMaxPerRun: 20
    activityCharLimit: 600
    activityPhaseLabel: true
    activityPhaseMaxPerRun: 5
```

You can also set these fields under `endpoints.all` or a backing provider/custom endpoint. Values resolve independently, with the first defined value winning in this order: `endpoints.all`, the public `agents` endpoint, then the backing provider or custom endpoint. If `activityEndpoint` is unknown, LibreChat logs a warning and uses the Agent's provider instead.

Parent phase summaries are independent of child activity labels: `activityPhaseLabel` can be enabled with or without `activityLabel`. LibreChat spends a phase-summary model call only for a phase containing at least two logical activities. `activityCharLimit` also bounds the evidence used for phase summaries. Phase model selection resolves through `activityPhaseModel`, `activityModel`, the originating endpoint's `titleModel`, then the current run model; phase endpoint selection resolves through `activityPhaseEndpoint`, `activityEndpoint`, then the Agent provider. An unknown configured phase endpoint logs a warning and falls back to the Agent provider.

## Live Reasoning Labels

Live reasoning labels replace the generic **Thinking** or **Thoughts** heading with a short, evolving orientation for sufficiently long top-level Agent reasoning. LibreChat updates the existing reasoning heading in place and does not add or shift message content parts. Nested subagent reasoning is not labeled by this setting.

<OptionTable
  options={[
    [
      'reasoningLabel',
      'Boolean',
      'Enables live model-generated headings for top-level Agent reasoning on this endpoint.',
      'false',
    ],
    [
      'reasoningLabelModel',
      'String',
      'Model used to generate labels. Falls back to activityModel, the originating endpoint titleModel, then the Agent run model. Use current_model to select the run model explicitly.',
      '',
    ],
    [
      'reasoningLabelEndpoint',
      'String',
      'Endpoint whose credentials receive the bounded reasoning snapshot. Falls back to activityEndpoint, then the Agent provider.',
      '',
    ],
    [
      'reasoningLabelPrompt',
      'String',
      'Overrides the prompt used to generate the live heading.',
      '',
    ],
    [
      'reasoningLabelMinChars',
      'Positive integer',
      'Visible reasoning characters required before the first label call.',
      '500',
    ],
    [
      'reasoningLabelUpdateChars',
      'Positive integer',
      'New reasoning characters required between streaming label revisions.',
      '400',
    ],
    [
      'reasoningLabelUpdateIntervalMs',
      'Non-negative integer',
      'Minimum milliseconds between streaming label revisions.',
      '3000',
    ],
    [
      'reasoningLabelMaxPerRun',
      'Positive integer',
      'Maximum reasoning-label provider calls attempted per Agent response.',
      '8',
    ],
  ]}
/>

```yaml filename="endpoints / agents / live reasoning labels"
endpoints:
  agents:
    reasoningLabel: true
    reasoningLabelEndpoint: 'openAI'
    reasoningLabelModel: 'gpt-4.1-nano'
    reasoningLabelMinChars: 500
    reasoningLabelUpdateChars: 400
    reasoningLabelUpdateIntervalMs: 3000
    reasoningLabelMaxPerRun: 8
```

You can set these fields under `endpoints.all`, the public `agents` endpoint, or a backing provider/custom endpoint. Each field resolves independently, with the first defined value winning in this order: `endpoints.all`, `endpoints.agents`, then the backing provider or custom endpoint.

Model selection resolves through `reasoningLabelModel`, `activityModel`, the originating endpoint's `titleModel`, then the current Agent run model. Endpoint selection resolves through `reasoningLabelEndpoint`, `activityEndpoint`, then the Agent provider. An unknown label endpoint logs a warning and falls back to the Agent provider.

Each call sends at most 4,000 characters of visible reasoning to the resolved label endpoint, which can be a different provider from the Agent. Labels are limited to 120 characters. Streaming revisions observe both the character and interval thresholds; at completion, a meaningful tail of at least 120 new characters can trigger a final rewrite immediately. Every attempted revision counts toward `reasoningLabelMaxPerRun` and is a separate model call with its own token usage and cost.

When Langfuse tracing is enabled, the bounded reasoning snapshot is recorded as the label generation's input unless the active redaction policy suppresses the label call. Review the destination endpoint and tracing policy before enabling this feature for sensitive reasoning content.

## maxToolResultChars

**Key:**

<OptionTable
  options={[
    [
      'maxToolResultChars',
      'Number',
      'Limits the maximum number of characters in tool call results sent to the model. Must be a positive number.',
      'maxToolResultChars: 50000',
    ],
  ]}
/>

**Default:** No limit

**Notes:**

- Helps prevent excessively large tool outputs from consuming too many tokens
- Applies to all tool call results for the endpoint

**Example:**

```yaml filename="maxToolResultChars"
endpoints:
  all:
    maxToolResultChars: 50000
```

## headers

**Key:**

<OptionTable
  options={[
    [
      'headers',
      'Object/Dictionary',
      'Custom request headers forwarded to supported built-in provider endpoints.',
      'Useful for AI gateways and reverse proxies that consume metadata headers while LibreChat keeps provider-native request formatting.',
    ],
  ]}
/>

**Supported endpoints:** `openAI`, `anthropic`, `google`, and `all`.

**Example:**

```yaml filename="endpoints / headers"
endpoints:
  all:
    headers:
      X-App: 'librechat'
  anthropic:
    headers:
      X-Conversation-Id: '{{LIBRECHAT_BODY_CONVERSATIONID}}'
```

**Notes:**

- Values support `${ENV_VAR}`, `{{LIBRECHAT_USER_*}}`, and request-body placeholders such as `{{LIBRECHAT_BODY_CONVERSATIONID}}`.
- Agent model requests additionally support `{{LIBRECHAT_USER_TENANT_ID}}` and `{{LIBRECHAT_USER_TENANTID}}`. Both resolve from the authoritative request-scoped tenant for root Agents, Subagents, activity labels, memory, and summarization calls, and become an empty string when the run has no tenant. This tenant value is intentionally limited to model headers and is not added to general user-placeholder consumers.
- At the final outbound resolution step, recognized user, request-body, and OpenID placeholders without a value become empty strings instead of being forwarded as template text. Unknown placeholder names remain unchanged so configuration typos are visible.
- Endpoint-level headers override `endpoints.all.headers` on key collisions.
- Provider-managed auth and required beta/protocol headers remain authoritative. Anthropic beta values are merged so custom beta flags do not clobber required provider flags.
- Headers are also forwarded for supported provider model-list requests.
- Use metadata headers behind a gateway or reverse proxy that consumes them. Native provider APIs typically ignore unknown headers.

---

**Notes:**

- All settings shown on this page can be configured individually per endpoint or globally using the `all` key
- When using the `all` configuration, it will override the corresponding settings in ALL individual endpoints
- The `all` key does not accept `baseURL`
- Settings not defined in `all` will preserve their individual endpoint values
- For `streamRate`: Values between 25-40 are recommended where endpoint-specific smoothing is desired
- Using a higher stream rate is a must when serving the app to many users at scale

**Example of Override Behavior:**

```yaml
endpoints:
  openAI:
    streamRate: 25 # This will be preserved
    titleModel: 'gpt-4' # This will be overridden
    titleConvo: false # This will be overridden

  all:
    titleConvo: true
    titleModel: 'gpt-3.5-turbo'
    # streamRate not defined here, so individual values are kept
```

---

# Endpoint Settings

- [Custom Endpoints](/docs/configuration/librechat_yaml/object_structure/custom_endpoint)
- [OpenAI](/docs/configuration/pre_configured_ai/openai)
- [Anthropic](/docs/configuration/pre_configured_ai/anthropic)
- [Bedrock](/docs/configuration/pre_configured_ai/bedrock)
- [Google](/docs/configuration/pre_configured_ai/google)
- [Azure OpenAI](/docs/configuration/librechat_yaml/object_structure/azure_openai)
- [Assistants](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint)


# Custom Endpoint Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/custom_endpoint)

Each endpoint in the `custom` array should have the following structure:

## Example

```yaml filename="Endpoint Object Structure"
endpoints:
  custom:
    # Example using Mistral AI API
    - name: 'Mistral'
      apiKey: '${YOUR_ENV_VAR_KEY}'
      baseURL: 'https://api.mistral.ai/v1'
      models:
        default: ['mistral-tiny', 'mistral-small', 'mistral-medium', 'mistral-large-latest']
      titleConvo: true
      titleTiming: 'immediate'
      titleModel: 'mistral-tiny'
      modelDisplayLabel: 'Mistral'
      # customParams:
      #   reasoningFormat: reasoning_object
      #   reasoningKey: reasoning_content
      # tokenConfig:
      #   mistral-large-latest:
      #     prompt: 2
      #     completion: 6
      #     context: 128000
      # addParams:
      #   safe_prompt: true # Mistral specific value for moderating messages
      # NOTE: For Mistral, it is necessary to drop the following parameters or you will encounter a 422 Error:
      dropParams: ['stop', 'user', 'frequency_penalty', 'presence_penalty']

    # Example using the native Anthropic Messages API
    - name: 'Claude-Compatible'
      provider: 'anthropic'
      apiKey: '${ANTHROPIC_API_KEY}'
      baseURL: 'https://api.anthropic.com'
      headers:
        anthropic-version: '2023-06-01'
      models:
        default: ['claude-sonnet-4-5', 'claude-opus-4-5']
        fetch: false
      titleConvo: true
      titleModel: 'claude-sonnet-4-5'
      modelDisplayLabel: 'Claude (Compatible)'
```

## name

**Key:**

<OptionTable
  options={[
    [
      'name',
      'String',
      'A unique name for the endpoint.',
      'Will be used as the "title" in the Endpoints Selector',
    ],
  ]}
/>

**Required**

**Example:**

```yaml filename="endpoints / custom / name"
name: 'Mistral'
```

## apiKey

**Key:**

<OptionTable
  options={[
    [
      'apiKey',
      'String (apiKey | "user_provided")',
      'Your API key for the service. Can reference an environment variable, or allow user to provide the value.',
      "It's highly recommended to use the env. variable reference for this field, i.e. `${YOUR_VARIABLE}`",
    ],
  ]}
/>

**Required**

**Example:**

```yaml filename="endpoints / custom / apiKey"
apiKey: '${MISTRAL_API_KEY}'
```

or

```yaml filename="endpoints / custom / apiKey"
apiKey: 'your_api_key'
```

or

```yaml filename="endpoints / custom / apiKey"
apiKey: 'user_provided'
```

When a custom endpoint is written through the administrator configuration API, LibreChat encrypts a literal `apiKey` at rest and returns only an `apiKeyPreview`. `${ENV_VAR}` references and `user_provided` remain readable passthrough values. For endpoints defined directly in `librechat.yaml`, prefer an environment reference as shown above.

## baseURL

**Key:**

<OptionTable
  options={[
    [
      'baseURL',
      'String (baseURL | "user_provided")',
      'Base URL for the API. Can reference an environment variable, or allow user to provide the value.',
      "It's highly recommended to use the env. variable reference for this field, i.e. `${YOUR_VARIABLE}`",
    ],
  ]}
/>

**Required**

**Example:**

```yaml filename="endpoints / custom / baseURL"
baseURL: 'https://api.mistral.ai/v1'
```

or

```yaml filename="endpoints / custom / baseURL"
baseURL: '${MISTRAL_BASE_URL}'
```

or

```yaml filename="endpoints / custom / baseURL"
baseURL: 'user_provided'
```

**Notes:**

- If the `baseURL` you set is the full completions endpoint, you can set the [directEndpoint](#directendpoint) field to `true` to use it directly.
  - This is necessary because the app appends "/chat/completions" or "/completion" to the `baseURL` by default.
- When using [`provider: anthropic`](#provider), set `baseURL` to the API root that the Anthropic SDK should call, such as `https://api.anthropic.com` or your gateway root. LibreChat uses the native Anthropic `/v1/messages` path for that provider.
- On OpenAI-compatible custom endpoints, model IDs containing `claude` use Claude document restrictions: PDFs and text documents are accepted, while non-PDF binary document types are skipped before the request is sent.

## provider

**Key:**

<OptionTable
  options={[
    [
      'provider',
      'String',
      'Routes a custom endpoint through a native provider client instead of the default OpenAI-compatible client.',
      'Currently supports `anthropic`.',
    ],
  ]}
/>

**Default:** omitted, which uses the OpenAI-compatible custom endpoint path.

**Supported Values:**

- **`"anthropic"`** - Uses the native Anthropic `/v1/messages` client with this endpoint's `baseURL`, `apiKey`, `headers`, `addParams`, `dropParams`, and `customParams.paramDefinitions`.

**Example:**

```yaml filename="endpoints / custom / provider"
endpoints:
  custom:
    - name: 'Claude-Compatible'
      provider: 'anthropic'
      apiKey: '${ANTHROPIC_API_KEY}'
      baseURL: 'https://api.anthropic.com'
      headers:
        anthropic-version: '2023-06-01'
      models:
        default:
          - 'claude-sonnet-4-5'
          - 'claude-opus-4-5'
        fetch: false
      titleConvo: true
      titleModel: 'claude-sonnet-4-5'
      modelDisplayLabel: 'Claude (Compatible)'
```

**Notes:**

- Use `provider: anthropic` for Anthropic itself or Anthropic-compatible gateways that speak the native Messages API.
- List models explicitly under `models.default`; OpenAI-style `models.fetch` is not used for native Anthropic custom endpoints.
- The provider implies Anthropic UI parameters unless you explicitly set a different `customParams.defaultParamsEndpoint`.
- Endpoints without `provider` keep the OpenAI-compatible behavior.

## iconURL

**Key:**

<OptionTable
  options={[
    [
      'iconURL',
      'String',
      'Image URL, public asset path, or built-in endpoint icon key to use as the endpoint icon.',
      '',
    ],
  ]}
/>

**Default:** `""`

**Example:**

```yaml filename="endpoints / custom / iconURL"
iconURL: https://github.com/danny-avila/LibreChat/raw/main/docs/assets/LibreChat.svg
```

or reuse a built-in endpoint icon:

```yaml filename="endpoints / custom / iconURL"
iconURL: openAI
```

**Notes:**

- Do not set a custom endpoint `name` to a built-in endpoint name just to reuse an icon. Custom endpoint names must be unique and should not use default endpoint values such as:
  - "openAI" | "azureOpenAI" | "google" | "anthropic" | "assistants" | "azureAssistants" | "agents" | "bedrock"
- To use a project-included endpoint icon, keep the custom endpoint `name` unique and set `iconURL` to one of the built-in endpoint keys instead.
  - "openAI" | "azureOpenAI" | "google" | "anthropic" | "assistants" | "azureAssistants" | "agents" | "bedrock"
- To use a custom image, set `iconURL` to an image URL or a path served by LibreChat, such as `/assets/my-icon.svg`.
- There are also "known endpoints" (case-insensitive), which have icons provided. If your endpoint `name` matches the following names, you should omit this field:
  - "Anyscale"
  - "APIpie"
  - "Cohere"
  - "Deepseek"
  - "Fireworks"
  - "groq"
  - "Helicone"
  - "Huggingface"
  - "Mistral"
  - "MLX"
  - "Moonshot"
  - "ollama"
  - "OpenRouter"
  - "Perplexity"
  - "Qwen"
  - "ShuttleAI"
  - "together.ai"
  - "Unify"
  - "xai"

## models

**Key:**

<OptionTable options={[['models', 'Object', 'Configuration for models.', '']]} />

**Required**

**Properties:**

### default

**Key:**

<OptionTable
  options={[
    [
      'default',
      'Array of Strings',
      'An array of strings indicating the default models to use.',
      'If fetching models fails, these defaults are used as a fallback.',
    ],
  ]}
/>

**Required**

**Example:**

```yaml filename="endpoints / custom / models / default"
default:
  - 'mistral-tiny'
  - 'mistral-small'
  - 'mistral-medium'
```

### fetch

**Key:**

<OptionTable
  options={[
    [
      'fetch',
      'Boolean',
      'When set to `true`, attempts to fetch a list of models from the API.',
      'May cause slowdowns during initial use of the app if the response is delayed. Defaults to `false`.',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml filename="endpoints / custom / models / fetch"
fetch: true
```

### userIdQuery

**Key:**

<OptionTable
  options={[
    [
      'userIdQuery',
      'Boolean',
      'When set to `true`, adds the LibreChat user ID as a query parameter to the API models request.',
      '',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml filename="endpoints / custom / models / userIdQuery"
userIdQuery: true
```

## titleConvo

**Key:**

<OptionTable
  options={[['titleConvo', 'Boolean', 'Enables title conversation when set to `true`.', '']]}
/>

**Default:** `false`

**Example:**

```yaml filename="endpoints / custom / titleConvo"
titleConvo: true
```

## titleTiming

**Key:**

<OptionTable
  options={[
    [
      'titleTiming',
      'String',
      'Controls when conversation titles are generated. Valid values: "immediate" or "final".',
      'Defaults to "immediate".',
    ],
  ]}
/>

**Default:** `"immediate"`

**Available Values:**

- **`"immediate"`** - Generates the title as soon as the request starts, in parallel with the model response, using the user's first message.
- **`"final"`** - Defers title generation until the full response completes. This preserves the legacy behavior.

**Example:**

```yaml filename="endpoints / custom / titleTiming"
titleTiming: 'final'
```

## titleMethod

**Key:**

<OptionTable
  options={[
    [
      'titleMethod',
      'String',
      'Controls the method used for generating conversation titles.',
      'Valid values: "completion" (default), "structured", "functions" (legacy alias for "structured")',
    ],
  ]}
/>

**Default:** `"completion"`

**Available Methods:**

- **`"completion"`** - Uses standard completion API without tools/functions. Compatible with most LLMs.
- **`"structured"`** - Uses structured output for title generation. Requires provider/model support.
- **`"functions"`** - Legacy alias for "structured". Functionally identical.

**Example:**

```yaml filename="endpoints / custom / titleMethod"
titleMethod: 'completion'
```

## titleModel

**Key:**

<OptionTable
  options={[
    [
      'titleModel',
      'String',
      'Specifies the model to use for titles.',
      'Defaults to "gpt-3.5-turbo" if omitted. May cause issues if "gpt-3.5-turbo" is not available. You can also dynamically use the current conversation model by setting it to "current_model".',
    ],
  ]}
/>

**Default:** `"gpt-3.5-turbo"`

**Example:**

```yaml filename="endpoints / custom / titleModel"
titleModel: 'mistral-tiny'
```

```yaml filename="endpoints / custom / titleModel"
titleModel: 'current_model'
```

## titlePrompt

**Key:**

<OptionTable
  options={[
    [
      'titlePrompt',
      'String',
      'Custom prompt for title generation. Must include {convo} placeholder.',
      'Allows full control over how titles are generated.',
    ],
  ]}
/>

**Default:**

```
Analyze this conversation and provide:
1. The detected language of the conversation
2. A concise title in the detected language (5 words or less, no punctuation or quotation)

{convo}
```

**Notes:**

- Must always include the `{convo}` placeholder
- The `{convo}` placeholder will be replaced with the formatted conversation

**Example:**

```yaml filename="endpoints / custom / titlePrompt"
titlePrompt: "Create a brief, descriptive title for the following conversation:\n\n{convo}\n\nTitle:"
```

## titlePromptTemplate

**Key:**

<OptionTable
  options={[
    [
      'titlePromptTemplate',
      'String',
      'Template for formatting the conversation content that replaces {convo} in titlePrompt.',
      'Must include {input} and {output} placeholders.',
    ],
  ]}
/>

**Default:** `"User: {input}\nAI: {output}"`

**Notes:**

- Must include both `{input}` and `{output}` placeholders
- Controls how the conversation is formatted when inserted into `titlePrompt`

**Example:**

```yaml filename="endpoints / custom / titlePromptTemplate"
titlePromptTemplate: "Human: {input}\n\nAssistant: {output}"
```

## titleEndpoint

**Key:**

<OptionTable
  options={[
    [
      'titleEndpoint',
      'String',
      'Specifies an alternative endpoint to use for title generation.',
      'Allows using a different model/endpoint for titles.',
    ],
  ]}
/>

**Default:** Uses the current custom endpoint

**Accepted Values:**

- `openAI`
- `azureOpenAI`
- `google`
- `anthropic`
- `bedrock`
- Another custom endpoint name

**Example:**

```yaml filename="endpoints / custom / titleEndpoint"
# Use a different custom endpoint for titles
endpoints:
  custom:
    - name: 'my-chat-endpoint'
      apiKey: '${CHAT_API_KEY}'
      baseURL: 'https://api.example.com/v1/chat'
      models:
        default: ['gpt-4']
      titleEndpoint: 'my-title-endpoint'

    - name: 'my-title-endpoint'
      apiKey: '${TITLE_API_KEY}'
      baseURL: 'https://api.example.com/v1/title'
      models:
        default: ['gpt-3.5-turbo']
```

## modelDisplayLabel

**Key:**

<OptionTable
  options={[
    [
      'modelDisplayLabel',
      'String',
      'The label displayed in messages next to the Icon for the current AI model.',
      'The display order is: 1. Custom name set via preset (if available), 2. Label derived from the model name (if applicable), 3. This value is used if the above are not specified. Defaults to "AI".',
    ],
  ]}
/>

**Default:** `"AI"`

**Example:**

```yaml filename="endpoints / custom / modelDisplayLabel"
modelDisplayLabel: 'Mistral'
```

## addParams

**Key:**

<OptionTable
  options={[
    [
      'addParams',
      'Object/Dictionary',
      'Adds additional parameters to requests. Values can be strings, numbers, booleans, arrays, or nested objects. Supports provider tool toggles such as `web_search: true` and Google `url_context: true`.',
      'Adds/Overrides parameters. Useful for specifying API-specific options.',
    ],
  ]}
/>

**Example:**

```yaml filename="endpoints / custom / addParams"
addParams:
  safe_prompt: true
  max_tokens: 2048
```

**Notes:**

- The `addParams` field allows you to include additional parameters that are not part of the default payload(see the ["Default Parameters"](/docs/configuration/librechat_yaml/object_structure/default_params) section). This is particularly useful for API-specific options.

## dropParams

**Key:**

<OptionTable
  options={[
    [
      'dropParams',
      'Array/List of Strings',
      'Removes default parameters from requests.',
      'Excludes specified default parameters. Useful for APIs that do not accept or recognize certain parameters.',
    ],
  ]}
/>

**Example:**

```yaml filename="endpoints / custom / dropParams"
dropParams:
  - 'stop'
  - 'user'
  - 'frequency_penalty'
  - 'presence_penalty'
```

**Note:**

- The `dropParams` field allows you to remove ["Default Parameters"](/docs/configuration/librechat_yaml/object_structure/default_params) that are sent with every request. This is helpful when working with APIs that do not accept or recognize certain parameters.

## customParams

**Key:**

<OptionTable
  options={[
    [
      'customParams',
      'Object/Dictionary',
      'Defines custom endpoint behavior and settings metadata that are not part of the provider request body.',
      'Used for endpoint-specific configuration such as reasoning parameter shape.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    [
      'defaultParamsEndpoint',
      'String',
      'Endpoint defaults used for request parameter metadata. Defaults to `custom`. When `provider: anthropic` is set and this field is omitted, LibreChat uses the Anthropic parameter set.',
      'defaultParamsEndpoint: custom',
    ],
    [
      'reasoningFormat',
      'String',
      'Controls how reasoning parameters are sent to OpenAI-compatible custom endpoints. Valid values: `reasoning_effort`, `reasoning_object`, `disabled`.',
      'reasoningFormat: reasoning_object',
    ],
    [
      'reasoningKey',
      'String',
      'Controls which response key is read for provider reasoning content. Valid values: `reasoning` or `reasoning_content`.',
      'reasoningKey: reasoning_content',
    ],
    [
      'includeReasoningContent',
      'Boolean',
      'Replays provider `reasoning_content` within tool-call turns for OpenAI-compatible custom endpoints that require it.',
      'includeReasoningContent: true',
    ],
    [
      'includeReasoningHistory',
      'Boolean',
      'Reconstructs `reasoning_content` from persisted conversation history across turns. Implies `includeReasoningContent`.',
      'includeReasoningHistory: true',
    ],
    [
      'paramDefinitions',
      'Array/List',
      'Custom setting definitions for this endpoint.',
      'See default parameter definitions.',
    ],
  ]}
/>

**Reasoning Formats:**

- **`reasoning_effort`** - Sends the legacy `reasoning_effort` parameter.
- **`reasoning_object`** - Sends a `reasoning` object, such as `{ effort, summary }`, for providers that follow the newer OpenAI-compatible shape.
- **`disabled`** - Suppresses reasoning parameters even when a user or model spec selects reasoning.

**Reasoning replay:**

- Use `includeReasoningContent: true` for OpenAI-compatible providers that require assistant `reasoning_content` to be replayed during tool-call turns.
- Use `includeReasoningHistory: true` only for providers that also require `reasoning_content` reconstructed from persisted history across later turns, such as some Xiaomi MiMo or Kimi-compatible gateways.

**Anthropic provider note:**

Use [`provider: anthropic`](#provider) when the custom endpoint should use the native Anthropic Messages API. Use `customParams.defaultParamsEndpoint: anthropic` without `provider` only when you still need the OpenAI-compatible custom endpoint path but want Anthropic-style parameter metadata and request adaptation.

**Example:**

```yaml filename="endpoints / custom / customParams"
customParams:
  reasoningFormat: reasoning_object
  reasoningKey: reasoning_content
  includeReasoningContent: true
```

## tokenConfig

**Key:**

<OptionTable
  options={[
    [
      'tokenConfig',
      'Object/Dictionary',
      'Defines model-specific context windows and per-million-token rates for this custom endpoint.',
      'Used by context usage, visible cost breakdowns, balance transactions, and multi-endpoint agent billing.',
    ],
  ]}
/>

Each key is a model name. Each model entry supports:

<OptionTable
  options={[
    ['prompt', 'Number', 'Prompt/input token rate per million tokens.', 'Required'],
    ['completion', 'Number', 'Completion/output token rate per million tokens.', 'Required'],
    ['context', 'Number', 'Maximum context window for the model.', 'Required'],
    ['cacheRead', 'Number', 'Cached input read rate per million tokens.', 'Optional'],
    ['cacheWrite', 'Number', 'Cached input write rate per million tokens.', 'Optional'],
  ]}
/>

**Example:**

```yaml filename="endpoints / custom / tokenConfig"
tokenConfig:
  gpt-4o-mini:
    prompt: 0.15
    completion: 0.6
    context: 128000
    cacheRead: 0.075
    cacheWrite: 0.15
```

**Notes:**

- Rates are expressed per million tokens in USD before any [`interface.currency`](/docs/configuration/librechat_yaml/object_structure/interface#currency) conversion is applied for display.
- The model name must match the model value sent through the custom endpoint.
- For Agents using multiple endpoints, the matching endpoint/model token config is used when recording usage and cost.

## headers

**Key:**

<OptionTable
  options={[
    [
      'headers',
      'Object/Dictionary',
      'Adds additional headers to requests. All header values must be strings. Supports dynamic user field substitution with `{{LIBRECHAT_USER_*}}`, request body placeholders with `{{LIBRECHAT_BODY_*}}`, and environment variables with `${ENV_VAR}`.',
      'The `headers` object specifies custom headers for requests. Useful for authentication and setting content types.',
    ],
  ]}
/>

**Example:**

```yaml filename="endpoints / custom / headers"
headers:
  x-api-key: '${ENVIRONMENT_VARIABLE}'
  Content-Type: 'application/json'
  X-User-ID: '{{LIBRECHAT_USER_ID}}'
  X-User-Email: '{{LIBRECHAT_USER_EMAIL}}'
```

**Note:** Supports dynamic environment variable values, which use the format: `"${VARIABLE_NAME}"`.

At the final outbound resolution step, recognized `{{LIBRECHAT_USER_*}}`, `{{LIBRECHAT_BODY_*}}`, and `{{LIBRECHAT_OPENID_*}}` placeholders without a value become empty strings instead of being forwarded as template text. Unknown placeholder names remain unchanged so configuration typos are visible.

When `models.fetch: true` is used, these headers are also resolved and forwarded to the model-list request for admin-controlled base URLs. A configured `Authorization` header takes precedence over the endpoint `apiKey` fallback, which is useful for auth-aware proxies that return per-user model lists. If `baseURL: "user_provided"` is configured, LibreChat does not forward configured header templates to the user-provided destination. For `provider: anthropic`, headers are forwarded through the native Anthropic client instead of the OpenAI-compatible client.

**Available User Field Placeholders:**

| Placeholder                           | User Field         | Type             | Description                                                 |
| ------------------------------------- | ------------------ | ---------------- | ----------------------------------------------------------- |
| `{{LIBRECHAT_USER_ID}}`               | `id`               | String           | User's unique identifier                                    |
| `{{LIBRECHAT_USER_NAME}}`             | `name`             | String           | User's display name                                         |
| `{{LIBRECHAT_USER_USERNAME}}`         | `username`         | String           | User's username                                             |
| `{{LIBRECHAT_USER_EMAIL}}`            | `email`            | String           | User's email address                                        |
| `{{LIBRECHAT_USER_PROVIDER}}`         | `provider`         | String           | Authentication provider (e.g., "email", "google", "github") |
| `{{LIBRECHAT_USER_ROLE}}`             | `role`             | String           | User's role (e.g., "user", "admin")                         |
| `{{LIBRECHAT_USER_GOOGLEID}}`         | `googleId`         | String           | Google account ID                                           |
| `{{LIBRECHAT_USER_FACEBOOKID}}`       | `facebookId`       | String           | Facebook account ID                                         |
| `{{LIBRECHAT_USER_OPENIDID}}`         | `openidId`         | String           | OpenID account ID                                           |
| `{{LIBRECHAT_USER_SAMLID}}`           | `samlId`           | String           | SAML account ID                                             |
| `{{LIBRECHAT_USER_LDAPID}}`           | `ldapId`           | String           | LDAP account ID                                             |
| `{{LIBRECHAT_USER_GITHUBID}}`         | `githubId`         | String           | GitHub account ID                                           |
| `{{LIBRECHAT_USER_DISCORDID}}`        | `discordId`        | String           | Discord account ID                                          |
| `{{LIBRECHAT_USER_APPLEID}}`          | `appleId`          | String           | Apple account ID                                            |
| `{{LIBRECHAT_USER_EMAILVERIFIED}}`    | `emailVerified`    | Boolean → String | Email verification status ("true" or "false")               |
| `{{LIBRECHAT_USER_TWOFACTORENABLED}}` | `twoFactorEnabled` | Boolean → String | 2FA status ("true" or "false")                              |
| `{{LIBRECHAT_USER_TERMSACCEPTED}}`    | `termsAccepted`    | Boolean → String | Terms acceptance status ("true" or "false")                 |
| `{{LIBRECHAT_USER_TERMSACCEPTEDAT}}`  | `termsAcceptedAt`  | Date → String    | Terms acceptance timestamp                                  |

Agent model requests also support `{{LIBRECHAT_USER_TENANT_ID}}` and `{{LIBRECHAT_USER_TENANTID}}` in custom endpoint headers. Both aliases use the authoritative request-scoped tenant rather than a potentially stale user record, apply to root Agents, Subagents, and custom summarization calls, and resolve to an empty string when no tenant is present. They are not general user-field placeholders for non-Agent template consumers.

**Available Request Body Placeholders:**

| Placeholder                          | Body Field        | Type   | Description                     |
| ------------------------------------ | ----------------- | ------ | ------------------------------- |
| `{{LIBRECHAT_BODY_CONVERSATIONID}}`  | `conversationId`  | String | Current conversation identifier |
| `{{LIBRECHAT_BODY_PARENTMESSAGEID}}` | `parentMessageId` | String | Parent message identifier       |
| `{{LIBRECHAT_BODY_MESSAGEID}}`       | `messageId`       | String | Current message identifier      |

**Example using request body placeholders:**

```yaml filename="endpoints / custom / headers with body placeholders"
headers:
  X-Conversation-ID: '{{LIBRECHAT_BODY_CONVERSATIONID}}'
  X-Parent-Message-ID: '{{LIBRECHAT_BODY_PARENTMESSAGEID}}'
  X-Message-ID: '{{LIBRECHAT_BODY_MESSAGEID}}'
```

## directEndpoint

**Key:**

<OptionTable
  options={[
    [
      'directEndpoint',
      'Boolean',
      'When set to `true`, treats the configured `baseURL` as the completions endpoint to be used',
      '',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml filename="endpoints / custom / directEndpoint"
directEndpoint: true
```

## titleMessageRole

- **Options**: `"system"` | `"user"` | `"assistant"`

**Key:**

<OptionTable
  options={[
    [
      'titleMessageRole',
      'String',
      'Specifies the role value to use in the message payload for title generation. Must be one of: `"system"`, `"user"`, `"assistant"`.',
      'Defaults to "system" if omitted. May cause issues if "system" is not a valid value, which is sometimes the case for single message payloads, as it is for title generation.',
    ],
  ]}
/>

**Default:** `"system"`

**Example:**

```yaml filename="endpoints / custom / titleMessageRole"
titleMessageRole: 'user'
```


# Azure OpenAI Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/azure_openai)

Integrating Azure OpenAI Service with your application allows you to seamlessly utilize multiple deployments and region models hosted by Azure OpenAI. This section details how to configure the Azure OpenAI endpoint for your needs. 

**[For a detailed guide on setting up Azure OpenAI configurations, click here](/docs/configuration/librechat_yaml/ai_endpoints/azure)**

## Example Configuration

```yaml filename="Example Azure OpenAI Object Structure"
endpoints:
  azureOpenAI:
    titleModel: "gpt-4-turbo"
    groups:
      - group: "my-westus" # arbitrary name
        apiKey: "${WESTUS_API_KEY}"
        instanceName: "actual-instance-name" # name of the resource group or instance
        version: "2023-12-01-preview"
        # baseURL: https://prod.example.com
        # additionalHeaders:
        #   X-Custom-Header: value
        models:
          gpt-4-vision-preview:
            deploymentName: gpt-4-vision-preview
            version: "2024-02-15-preview"
          gpt-3.5-turbo:
            deploymentName: gpt-35-turbo
          gpt-3.5-turbo-1106:
            deploymentName: gpt-35-turbo-1106
          gpt-4:
            deploymentName: gpt-4
          gpt-4-1106-preview:
            deploymentName: gpt-4-1106-preview
      - group: "my-eastus"
        apiKey: "${EASTUS_API_KEY}"
        instanceName: "actual-eastus-instance-name"
        deploymentName: gpt-4-turbo
        version: "2024-02-15-preview"
        baseURL: "https://gateway.ai.cloudflare.com/v1/cloudflareId/azure/azure-openai/${INSTANCE_NAME}/${DEPLOYMENT_NAME}" # uses env variables
        additionalHeaders:
          X-Custom-Header: value
        models:
          gpt-4-turbo: true
```

> **Note:** Azure OpenAI endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings), including `streamRate`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`.

## assistants

**Key:**
<OptionTable
  options={[
    ['assistants', 'Boolean', 'Enables or disables assistants for the Azure OpenAI endpoint. When set to `true`, activates assistants associated with this endpoint.', 'Choose one, either the official OpenAI API or Azure OpenAI API for assistants, not both.'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / assistants"
assistants: true
```

## groups

**Key:**
<OptionTable
  options={[
    ['groups', 'Array', 'Configuration for groups of models by geographic location or purpose. Each item in the `groups` array configures a set of models under a certain grouping, often by geographic region or distinct configuration.', ''],
  ]}
/>

**Default:** Not specified

**Note:** [See example above.](#example-configuration)


## Group Object Structure

Each item under `groups` is part of a list of records, each with the following fields:

### group

**Key:**
<OptionTable
  options={[
    ['group', 'String', 'Identifier for a group of models.', ''],
  ]}
/>

**Required:** yes

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / group"
"group": "my-westus"
```

### apiKey

**Key:**
<OptionTable
  options={[
    ['apiKey', 'String', 'The API key for accessing the Azure OpenAI Service.', 'It\'s highly recommended to use a custom env. variable reference for this field, i.e. `${YOUR_VARIABLE}`'],
  ]}
/>

**Required:** yes

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / apiKey"
apiKey: "${WESTUS_API_KEY}"
```

### instanceName

**Key:**
<OptionTable
  options={[
    ['instanceName', 'String', 'Name of the Azure instance. **Supports both domain formats**: `.openai.azure.com` (legacy) and `.cognitiveservices.azure.com` (new). You can specify either the full domain (e.g., `my-instance.cognitiveservices.azure.com`) or just the instance name (e.g., `my-instance`) for backward compatibility with the legacy `.openai.azure.com` format.', 'It\'s recommended to use a custom env. variable reference for this field, i.e. `${YOUR_VARIABLE}`'],
  ]}
/>

**Required:** yes

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / instanceName"
# Using just the instance name (legacy format applied)
instanceName: "my-westus"
# OR using the full domain (new format)
instanceName: "my-westus.cognitiveservices.azure.com"
```


### version

**Key:**
<OptionTable
  options={[
    ['version', 'String', 'API version.', 'It\'s recommended to use a custom env. variable reference for this field, i.e. `${YOUR_VARIABLE}`'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / version"
version: "2023-12-01-preview"
```

### baseURL

**Key:**
<OptionTable
  options={[
    ['baseURL', 'String', 'The base URL for the Azure OpenAI Service.', 'It\'s recommended to use a custom env. variable reference for this field, i.e. `${YOUR_VARIABLE}`'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / baseURL"
baseURL: "https://prod.example.com"
```

### additionalHeaders

**Key:**
<OptionTable
  options={[
    ['additionalHeaders', 'Dictionary', 'Additional headers for API requests. All header values must be strings.', 'It\'s recommended to use a custom env. variable reference for the values of field, as shown in the example. `api-key` header value is sent on every request.'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / additionalHeaders"
additionalHeaders:
  X-Custom-Header: ${YOUR_SECRET_CUSTOM_VARIABLE}
```

### serverless

**Key:**
<OptionTable
  options={[
    ['serverless', 'Boolean', 'Indicates the use of a serverless inference endpoint for Azure OpenAI chat completions. When set to `true`, specifies that the group is configured to use serverless inference endpoints as an Azure "Models as a Service" model.', 'More info [here](../ai_endpoints/azure.mdx#serverless-inference-endpoints)'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / serverless"
serverless: true
```

### addParams

**Key:**
<OptionTable
  options={[
    ['addParams', 'Object/Dictionary', 'Adds additional parameters to requests. Useful for specifying API-specific options.', ''],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / addParams"
addParams:
  safe_prompt: true
```

### dropParams

**Key:**
<OptionTable
  options={[
    ['dropParams', 'Array/List of Strings', 'Removes default parameters from requests. Excludes specified default parameters.', 'Default parameters are the standard request parameters LibreChat sends to the Azure OpenAI API.'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / dropParams"
dropParams: ["stop", "user", "frequency_penalty", "presence_penalty"]
```

### models

**Key:**
<OptionTable
  options={[
    ['models', '', 'Configuration for individual models within a group. Configures settings for each model, including deployment name and version.', 'Model configurations can adopt the group\'s deployment name and/or version when configured as a boolean (set to `true`) or an object for detailed settings of either of those fields.'],
  ]}
/>

**Default:** Not specified

**Example:**
```yaml filename="endpoints / azureOpenAI / groups / {group_item} / models"
models:
  gpt-4-vision-preview: 
    deploymentName: "arbitrary-deployment-name"
    version: "2024-02-15-preview"
```


# AWS Bedrock Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/aws_bedrock)

Integrating AWS Bedrock with your application allows you to seamlessly utilize multiple AI models hosted on AWS. This section details how to configure the AWS Bedrock endpoint for your needs.

## Example Configuration

```yaml filename="Example AWS Bedrock Object Structure"
endpoints:
  bedrock:
    titleModel: 'anthropic.claude-3-haiku-20240307-v1:0'
    streamRate: 35
    availableRegions:
      - 'us-east-1'
      - 'us-west-2'
    guardrailConfig:
      guardrailIdentifier: 'your-guardrail-id'
      guardrailVersion: '1'
      trace: 'enabled'
      streamProcessingMode: 'sync'
```

> **Note:** AWS Bedrock endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings), including `streamRate`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`. The settings shown below are specific to Bedrock or have Bedrock-specific defaults.

## titleModel

**Key:**

<OptionTable
  options={[
    [
      'titleModel',
      'String',
      'Specifies the model to use for generating conversation titles.',
      'Recommended: anthropic.claude-3-haiku-20240307-v1:0. Set to "current_model" to use the same model as the chat.',
    ],
  ]}
/>

**Default:** Not specified

**Example:**

```yaml filename="titleModel"
titleModel: 'anthropic.claude-3-haiku-20240307-v1:0'
```

## streamRate

**Key:**

<OptionTable
  options={[
    [
      'streamRate',
      'Number',
      'Sets the rate of processing each new token in milliseconds.',
      'This can help stabilize processing of concurrent requests and provide smoother frontend stream rendering.',
    ],
  ]}
/>

**Default:** Not specified

**Example:**

```yaml filename="streamRate"
streamRate: 35
```

## availableRegions

**Key:**

<OptionTable
  options={[
    [
      'availableRegions',
      'Array',
      'Specifies the AWS regions you want to make available for Bedrock.',
      'If provided, users will see a dropdown to select the region. If not selected, the default region is used.',
    ],
  ]}
/>

**Default:** Not specified

**Example:**

```yaml filename="availableRegions"
availableRegions:
  - 'us-east-1'
  - 'us-west-2'
```

## models

**Key:**

<OptionTable
  options={[
    [
      'models',
      'Array of Strings',
      'Specifies custom model IDs available for the Bedrock endpoint.',
      'When provided, these models appear in the model selector for Bedrock.',
    ],
  ]}
/>

**Default:** Not specified (uses default Bedrock model list)

**Example:**

```yaml filename="models"
endpoints:
  bedrock:
    models:
      - 'anthropic.claude-sonnet-4-20250514-v1:0'
      - 'anthropic.claude-haiku-4-20250514-v1:0'
      - 'us.anthropic.claude-sonnet-4-20250514-v1:0'
```

## inferenceProfiles

**Key:**

<OptionTable
  options={[
    [
      'inferenceProfiles',
      'Object (Record)',
      'Maps model IDs to inference profile ARNs for cross-region inference. Keys are model IDs and values are the inference profile ARN or an environment variable reference.',
      'When a selected model matches a key, the corresponding ARN is used as the application inference profile.',
    ],
  ]}
/>

**Default:** Not specified

**Example:**

```yaml filename="inferenceProfiles"
endpoints:
  bedrock:
    inferenceProfiles:
      'us.anthropic.claude-sonnet-4-20250514-v1:0': '${BEDROCK_INFERENCE_PROFILE_CLAUDE_SONNET}'
      'anthropic.claude-3-7-sonnet-20250219-v1:0': 'arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/abc123'
```

**Notes:**

- Inference profiles enable cross-region inference, allowing you to route requests to models in different AWS regions
- Values support environment variable interpolation with `${ENV_VAR}` syntax
- The model ID in the key must match the model selected by the user in the UI
- Use with the `models` field to make cross-region model IDs available in the model selector
- For a complete guide on creating and managing inference profiles, see [AWS Bedrock Inference Profiles](/docs/configuration/pre_configured_ai/bedrock_inference_profiles)

**Combined Example:**

```yaml filename="Bedrock with inference profiles"
endpoints:
  bedrock:
    models:
      - 'us.anthropic.claude-sonnet-4-20250514-v1:0'
      - 'us.anthropic.claude-haiku-4-20250514-v1:0'
    inferenceProfiles:
      'us.anthropic.claude-sonnet-4-20250514-v1:0': '${BEDROCK_CLAUDE_SONNET_PROFILE}'
      'us.anthropic.claude-haiku-4-20250514-v1:0': '${BEDROCK_CLAUDE_HAIKU_PROFILE}'
```

## guardrailConfig

**Key:**

<OptionTable
  options={[
    [
      'guardrailConfig',
      'Object',
      'Configuration for AWS Bedrock Guardrails to filter and moderate model inputs and outputs.',
      'Optional. When configured, all Bedrock requests will be validated against the specified guardrail.',
    ],
  ]}
/>

**Sub-keys:**

<OptionTable
  options={[
    [
      'guardrailIdentifier',
      'String',
      'The unique identifier of the guardrail to apply.',
      'Required when using guardrails.',
    ],
    [
      'guardrailVersion',
      'String',
      'The version of the guardrail to use.',
      'Required when using guardrails.',
    ],
    [
      'trace',
      'String',
      'Controls guardrail trace output for debugging. Options: "enabled", "enabled_full", or "disabled".',
      'Optional. Default: "disabled"',
    ],
    [
      'streamProcessingMode',
      'String',
      'Controls guardrail stream processing mode. Options: "sync" or "async".',
      'Optional. Default: "sync"',
    ],
  ]}
/>

**Example:**

```yaml filename="guardrailConfig"
endpoints:
  bedrock:
    guardrailConfig:
      guardrailIdentifier: 'abc123xyz'
      guardrailVersion: '1'
      trace: 'enabled'
      streamProcessingMode: 'sync'
```

**Notes:**

- Guardrails help ensure responsible AI usage by filtering harmful content, PII, and other sensitive information
- The `guardrailIdentifier` can be found in the AWS Bedrock console under Guardrails
- Set `trace` to `"enabled"` or `"enabled_full"` during development to see which guardrail policies are triggered
- Set `streamProcessingMode` to `"async"` to stream responses faster (at the cost of guardrail possibly allowing inappropriate content through until its scan completes)
- For production, set `trace` to `"disabled"` to reduce response payload size

## Notes

- AWS Bedrock authentication is configured through environment variables. You can use `BEDROCK_AWS_PROFILE`, the AWS SDK default credential provider chain, `BEDROCK_AWS_BEARER_TOKEN` for Bedrock API keys, or Bedrock-specific static credentials. See the [AWS Bedrock setup guide](/docs/configuration/pre_configured_ai/bedrock#authentication) for details.


# Anthropic Vertex AI Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/anthropic_vertex)

LibreChat supports running Anthropic Claude models through **Google Cloud Vertex AI**. This allows you to use Claude models with your existing Google Cloud infrastructure, billing, and credentials.

**[For quick setup using environment variables, see the Anthropic configuration guide](/docs/configuration/pre_configured_ai/anthropic#vertex-ai)**

## Benefits

- **Unified Billing:** Use your existing Google Cloud billing account
- **Enterprise Features:** Access Google Cloud's enterprise security and compliance features
- **Regional Compliance:** Deploy in specific regions to meet data residency requirements
- **Existing Infrastructure:** Leverage your current GCP service accounts and IAM policies

## Prerequisites

Before configuring Anthropic Vertex AI, ensure you have:

1. **Google Cloud Project** with the Vertex AI API enabled
2. **Service Account** with the `Vertex AI User` role (`roles/aiplatform.user`)
3. **Claude models** enabled in your [Vertex AI Model Garden](https://console.cloud.google.com/vertex-ai/model-garden)
4. **Service Account Key** (JSON file) downloaded and accessible to LibreChat

## Example Configuration

```yaml filename="Example Anthropic Vertex AI Configuration"
endpoints:
  anthropic:
    streamRate: 20
    titleModel: "claude-3.5-haiku"  # Use the visible model name (key from models config)

    vertex:
      region: "global"
      # serviceKeyFile: "/path/to/service-account.json"  # Optional, defaults to api/data/auth.json
      # projectId: "${VERTEX_PROJECT_ID}"  # Optional, auto-detected from service key

      # Model mapping: visible name -> Vertex AI deployment name
      models:
        claude-fable-5-1:
          deploymentName: claude-fable-5-1
        claude-opus-5:
          deploymentName: claude-opus-5
        claude-sonnet-5:
          deploymentName: claude-sonnet-5
        claude-3.7-sonnet:
          deploymentName: claude-3-7-sonnet-20250219
        claude-3.5-sonnet:
          deploymentName: claude-3-5-sonnet-v2@20241022
        claude-3.5-haiku:
          deploymentName: claude-3-5-haiku@20241022
```

> **Note:** Anthropic endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings), including `streamRate`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`.

---

## vertex

The `vertex` object contains all Vertex AI-specific configuration options.

### region

**Key:**
<OptionTable
  options={[
    ['region', 'String', 'The Google Cloud region where your Vertex AI endpoint is deployed.', 'Must be a region where Claude models are available on Vertex AI.'],
  ]}
/>

**Default:** `us-east5`

**Available Regions:**

- `global`
- `us`
- `eu`
- `us-east5`
- `us-central1`
- `europe-west1`
- `europe-west4`
- `asia-southeast1`

> **Important:** Claude Opus 4.7 and newer, Opus 5, Sonnet 5, and Fable/Mythos 5 or 5.1 require `global`, `us`, or `eu`. Specific regions such as `us-east5` serve Claude Sonnet 4.6 and earlier and return a model-not-found response for newer models. Use `global` for the broadest availability and no regional pricing premium, or `us`/`eu` when you need multi-region data residency.

**Example:**
```yaml filename="endpoints / anthropic / vertex / region"
region: "global"
```

### projectId

**Key:**
<OptionTable
  options={[
    ['projectId', 'String', 'The Google Cloud Project ID. Supports environment variable references.', 'Optional. If not specified, auto-detected from the service account key file.'],
  ]}
/>

**Default:** Auto-detected from service key file

**Example:**
```yaml filename="endpoints / anthropic / vertex / projectId"
projectId: "${GOOGLE_PROJECT_ID}"
```

### serviceKeyFile

**Key:**
<OptionTable
  options={[
    ['serviceKeyFile', 'String', 'Path to the Google Cloud service account key JSON file.', 'Can be absolute or relative to the LibreChat root directory.'],
  ]}
/>

**Default:** `api/data/auth.json` (or `GOOGLE_SERVICE_KEY_FILE` environment variable)

**Example:**
```yaml filename="endpoints / anthropic / vertex / serviceKeyFile"
serviceKeyFile: "/etc/secrets/gcp-service-account.json"
```

---

## models

The `models` field defines the available Claude models and maps user-friendly names to Vertex AI deployment IDs. This works similarly to [Azure OpenAI model mapping](/docs/configuration/librechat_yaml/object_structure/azure_openai#group-object-structure).

When `models` is omitted, LibreChat uses its built-in Vertex catalog and removes models incompatible with the configured `region`. An explicit `models` list or map is never filtered, so every deployment ID must be available in the selected location.

### Format Options

You can configure models in three ways:

#### Option 1: Simple Array

Use the actual Vertex AI model IDs directly. These will be shown as-is in the UI:

```yaml filename="Simple array format"
models:
  - "claude-fable-5-1"
  - "claude-opus-5"
  - "claude-sonnet-5"
  - "claude-sonnet-4-20250514"
  - "claude-3-7-sonnet-20250219"
  - "claude-3-5-haiku@20241022"
```

#### Option 2: Object with Custom Names (Recommended)

Map user-friendly names to Vertex AI deployment names:

```yaml filename="Object format with custom names"
models:
  claude-fable-5-1:        # Visible in UI
    deploymentName: claude-fable-5-1  # Actual Vertex AI model ID
  claude-opus-5:           # Visible in UI
    deploymentName: claude-opus-5  # Actual Vertex AI model ID
  claude-sonnet-5:
    deploymentName: claude-sonnet-5
  claude-3.5-haiku:
    deploymentName: claude-3-5-haiku@20241022
```

#### Option 3: Mixed Format with Default

Set a default deployment name and use boolean values for models that inherit it:

```yaml filename="Mixed format"
deploymentName: claude-sonnet-4-20250514  # Default deployment
models:
  claude-sonnet-4: true  # Uses default deploymentName
  claude-3.5-haiku:
    deploymentName: claude-3-5-haiku@20241022  # Override for this model
```

### Model Object Properties

<OptionTable
  options={[
    ['deploymentName', 'String', 'The actual Vertex AI model ID used for API calls.', 'Required for each model unless using boolean `true` with a group-level default.'],
  ]}
/>

**Example:**
```yaml filename="Model with deploymentName"
models:
  claude-sonnet-4:
    deploymentName: claude-sonnet-4-20250514
```

---

## Environment Variable Alternative

For simpler setups, you can configure Vertex AI using environment variables instead of YAML:

```bash filename=".env"
# Enable Vertex AI mode
ANTHROPIC_USE_VERTEX=true

# Vertex AI region (optional, defaults to us-east5)
ANTHROPIC_VERTEX_REGION=global

# Path to service account key (optional, defaults to api/data/auth.json)
GOOGLE_SERVICE_KEY_FILE=/path/to/service-account.json
```

> **Note:** When using environment variables, model mapping is not available and LibreChat includes its standard Anthropic model catalog. Set `ANTHROPIC_VERTEX_REGION` to `global`, `us`, or `eu` before selecting a modern model that is unavailable from a specific region.

---

## Complete Examples

### Basic Setup

Minimal configuration using defaults (Vertex AI is enabled by the presence of the `vertex` section):

```yaml filename="Basic Vertex AI Setup"
endpoints:
  anthropic:
    vertex:
      region: global
```

This uses:

- Region: `global`
- Service key: `api/data/auth.json` (or `GOOGLE_SERVICE_KEY_FILE` env var)
- Project ID: Auto-detected from service key
- Models: LibreChat's built-in Vertex catalog for the selected location

### Production Setup with Model Mapping

Full configuration with custom model names and titles:

```yaml filename="Production Vertex AI Setup"
endpoints:
  anthropic:
    streamRate: 20
    titleModel: "haiku"
    titleMethod: "completion"

    vertex:
      region: "global"
      serviceKeyFile: "${GOOGLE_SERVICE_KEY_FILE}"

      models:
        fable:
          deploymentName: claude-fable-5-1
        opus:
          deploymentName: claude-opus-5
        sonnet:
          deploymentName: claude-sonnet-5
        haiku:
          deploymentName: claude-3-5-haiku@20241022
```

### Multi-Region Setup

You can only configure one region per deployment. For multi-region needs, consider using separate LibreChat instances or custom endpoints.

---

## Troubleshooting

### Common Errors

**"Could not load the default credentials"**
- Ensure the service account key file exists at the specified path
- Check file permissions (must be readable by the LibreChat process)
- Verify the JSON file is valid and not corrupted

**"Permission denied" or "403 Forbidden"**
- Verify the service account has the `Vertex AI User` role
- Ensure Claude models are enabled in your Vertex AI Model Garden
- Check that the service account belongs to the correct project

**"Model not found"**
- Check that the model ID in `deploymentName` is correct
- Verify the model is available in your selected region
- Use `global`, `us`, or `eu` for Opus 4.7+, Opus 5, Sonnet 5, and Fable/Mythos 5
- Ensure the model is enabled in your Vertex AI Model Garden

### Region Issues

**"Invalid region" or "Region not supported"**
- Use one of the supported regions listed above
- Try using `global` region which provides automatic routing
- Check [Google Cloud's documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#regions) for the latest list of regions where Claude is available

**"Model not available in region"**
- Not all Claude models are available in all regions
- Switch to `global`, `us`, or `eu` for models newer than Sonnet 4.6
- Check the [Vertex AI Model Garden](https://console.cloud.google.com/vertex-ai/model-garden) to see which models are available in your region

**Latency issues**
- If you're experiencing high latency, try using a region geographically closer to your users
- The `global` region automatically routes to the nearest available region
- For production workloads with strict latency requirements, test different regions and choose the one with best performance for your use case

### Verifying Setup

1. Ensure your service account key is valid:
   ```bash
   gcloud auth activate-service-account --key-file=/path/to/key.json
   gcloud auth list
   ```

2. Test Vertex AI access:
   ```bash
   gcloud ai models list --region=us-east5
   ```

3. Verify Claude model access:
   ```bash
   curl -X POST \
     -H "Authorization: Bearer $(gcloud auth print-access-token)" \
     -H "Content-Type: application/json" \
     "https://us-east5-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT/locations/us-east5/publishers/anthropic/models/claude-3-5-haiku@20241022:rawPredict" \
     -d '{"anthropic_version": "vertex-2023-10-16", "max_tokens": 100, "messages": [{"role": "user", "content": "Hello"}]}'
   ```

---

## Notes

- Vertex AI and direct Anthropic API are mutually exclusive. When a `vertex` configuration section is present, the `ANTHROPIC_API_KEY` environment variable is ignored.
- Web search functionality is fully supported with Vertex AI.
- Prompt caching is supported via automatic header filtering for Vertex AI compatibility.
- Function calling and tool use work the same as with the direct Anthropic API.


# Assistants Endpoint Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/assistants_endpoint)

This page applies to both the `assistants` and `azureAssistants` endpoints.

**Note:** To enable `azureAssistants`, see the [Azure OpenAI Configuration](/docs/configuration/librechat_yaml/ai_endpoints/azure#using-assistants-with-azure) for more information.

## Example

```yaml filename="Assistants Endpoint"
endpoints:
  # azureAssistants: # <-- Azure-specific configuration has the same structure as `assistants`
    #  pollIntervalMs: 500
    #  timeoutMs: 10000

  assistants:
    disableBuilder: false
    # Use either `supportedIds` or `excludedIds` but not both
    supportedIds: ["asst_supportedAssistantId1", "asst_supportedAssistantId2"]
    # excludedIds: ["asst_excludedAssistantId"]
    # `privateAssistants` do not work with `supportedIds` or `excludedIds`
    # privateAssistants: false
    # (optional) Models that support retrieval, will default to latest known OpenAI models that support the feature
    # retrievalModels: ["gpt-4-turbo-preview"]
    # (optional) Assistant Capabilities available to all users. Omit the ones you wish to exclude. Defaults to list below.
    # capabilities: ["code_interpreter", "retrieval", "actions", "tools", "image_vision"]
```
> This configuration enables the builder interface for assistants, sets a polling interval of 500ms to check for run updates, and establishes a timeout of 10 seconds for assistant run operations.

## disableBuilder

**Key:**
<OptionTable
  options={[
    ['disableBuilder', 'Boolean', 'Controls the visibility and use of the builder interface for assistants.', 'When set to `true`, disables the builder interface for the assistant, limiting direct manual interaction.'],
  ]}
/>

**Default:** `false`

**Example:**
```yaml filename="endpoints / assistants / disableBuilder"
disableBuilder: false
```

## pollIntervalMs

**Key:**
<OptionTable
  options={[
    ['pollIntervalMs', 'Integer', 'Specifies the polling interval in milliseconds for checking run updates or changes in assistant run states.', 'Specifies the polling interval in milliseconds for checking assistant run updates.'],
  ]}
/>

**Default:** `2000`

**Example:**
```yaml filename="endpoints / assistants / pollIntervalMs"
pollIntervalMs: 2500
```
**Note:** Currently, this is only used by Azure Assistants. Higher values are recommended for Azure Assistants to avoid rate limiting errors.

## timeoutMs

**Key:**
<OptionTable
  options={[
    ['timeoutMs', 'Integer', 'Defines the maximum time in milliseconds that an assistant can run before the request is cancelled.', 'Sets a timeout in milliseconds for assistant runs. Helps manage system load by limiting total run operation time.'],
  ]}
/>

**Default:** `180000`

**Example:**
```yaml filename="endpoints / assistants / timeoutMs"
timeoutMs: 10000
```
**Note:** Defaults to 3 minutes (180,000 ms). Run operation times can range between 50 seconds to 2 minutes but also exceed this. If the `timeoutMs` value is exceeded, the run will be cancelled.

## supportedIds

**Key:**
<OptionTable
  options={[
    ['supportedIds', 'Array/List of Strings', 'List of supported assistant Ids', 'Use this or `excludedIds` but not both (the `excludedIds` field will be ignored if so).'],
  ]}
/>

**Example:**
```yaml filename="endpoints / assistants / supportedIds"
supportedIds:
  - "asst_supportedAssistantId1"
  - "asst_supportedAssistantId2"
```

## excludedIds

**Key:**
<OptionTable
  options={[
    ['excludedIds', 'Array/List of Strings', 'List of excluded assistant Ids', 'Use this or `supportedIds` but not both (the `excludedIds` field will be ignored if so).'],
  ]}
/>

**Example:**
```yaml filename="endpoints / assistants / excludedIds"
excludedIds:
  - "asst_excludedAssistantId1"
  - "asst_excludedAssistantId2"
```

## privateAssistants

**Key:**
<OptionTable
  options={[
    ['privateAssistants', 'Boolean', 'Controls whether assistants are private to the user that created them', 'Does not work with `supportedIds` or `excludedIds` (`supportedIds` and `excludedIds` will be ignored).'],
  ]}
/>

**Default:** `false`

**Example:**
```yaml filename="endpoints / assistants / privateAssistants"
privateAssistants: false
```

## retrievalModels

**Key:**
<OptionTable
  options={[
    ['retrievalModels', 'Array/List of Strings', 'Specifies the models that support retrieval for the assistants endpoint.', 'Defines the models that support retrieval capabilities for the assistants endpoint. By default, it uses the latest known OpenAI models that support the official Retrieval feature.'],
  ]}
/>

**Default:** `[]` (uses the latest known OpenAI models that support retrieval)

**Example:**
```yaml filename="endpoints / assistants / retrievalModels"
retrievalModels:
  - "gpt-4-turbo-preview"
```

## capabilities

**Key:**
<OptionTable
  options={[
    ['capabilities', 'Array/List of Strings', 'Specifies the assistant capabilities available to all users for the assistants endpoint.', 'Defines the assistant capabilities that are available to all users for the assistants endpoint. You can omit the capabilities you wish to exclude from the list.'],
  ]}
/>

**Default:** `["code_interpreter", "image_vision", "retrieval", "actions", "tools"]`

**Example:**
```yaml filename="endpoints / assistants / capabilities"
capabilities:
  - "code_interpreter"
  - "retrieval"
  - "actions"
  - "tools"
  - "image_vision"
```
**Note:** This field is optional. If omitted, the default behavior is to include all the capabilities listed in the example.

# Agents Endpoint Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/agents)

This page applies to the [`agents`](/docs/features/agents) endpoint.

## Example

```yaml filename="Agents Endpoint"
endpoints:
  agents:
    recursionLimit: 50
    maxRecursionLimit: 100
    maxSubagents: 20
    maxToolCallArgBytes: 65536
    maxDeltaEventsPerTurn: 100000
    maxToolCallArgBytesByTool:
      create_file: 131072
    disableBuilder: false
    # (optional) Agent Capabilities available to all users. Omit the ones you wish to exclude. Defaults to list below.
    # Opt-in capabilities: "programmatic_tools", "stateful_code_sessions", "run_in_background", and "tool_intents".
    # capabilities: ["deferred_tools", "execute_code", "file_search", "web_search", "artifacts", "subagents", "actions", "context", "skills", "memory", "ask_user_question", "tools", "chain", "ocr"]
    # (optional) File citation configuration for file_search capability
    maxCitations: 30 # Maximum total citations in responses (1-50)
    maxCitationsPerFile: 7 # Maximum citations from each file (1-10)
    minRelevanceScore: 0.45 # Minimum relevance score threshold (0.0-1.0)
    titleTiming: immediate
    activityLabel: true
    activityModel: gpt-4.1-nano
    activityPhaseLabel: true
    activityPhaseMaxPerRun: 5
    skills:
      maxCatalogSkills: 20
    statefulCodeSessions:
      allowedEnvironments: ['user', 'agent-user', 'conversation']
      environments:
        - id: managed-default
          name: Managed Code API
          type: managed
          baseURL: https://code.example.com/v1
          default: true
        - id: engineering-vm
          name: Engineering VM
          type: attached
          baseURL: https://code-bridge.example.com/v1
          pairing:
            workerId: engineering-vm
            tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
        - id: personal-workers
          name: Personal Code Workers
          type: attached
          baseURL: https://code-bridge.example.com/v1
          pairing:
            allowPrincipalWorkers: true
            tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
    # eventDriven:
    #   selfUrl: https://librechat.internal
    backgroundTasks:
      completionWakeups: true
    toolApproval:
      enabled: true
      mode: default
      deny: ['mcp:*:delete_*']
      ask: ['mcp:*:*']
    checkpointer:
      type: mongo
      ttl: 86400
    remoteApi:
      auth:
        oidc:
          enabled: false
```

> This configuration enables the builder interface for agents.

## recursionLimit

<OptionTable
  options={[
    [
      'recursionLimit',
      'Number',
      'Sets the default number of steps an agent can take in a run.',
      'Controls recursion depth to prevent infinite loops. When the limit is reached, LibreChat preserves the partial turn and offers Keep going or Answer now. This value can be configured from the UI up to maxRecursionLimit.',
    ],
  ]}
/>

**Default:** `25`

**Example:**

```yaml filename="endpoints / agents / recursionLimit"
recursionLimit: 50
```

For more information about agent steps, see [Max Agent Steps](/docs/features/agents#max-agent-steps).

## maxRecursionLimit

<OptionTable
  options={[
    [
      'maxRecursionLimit',
      'Number',
      'Sets the absolute maximum number of steps an agent can take in a run.',
      'Defines the upper limit for the recursionLimit that can be set from the UI. This prevents users from setting excessively high values.',
    ],
  ]}
/>

**Default:** If omitted, defaults to the value of recursionLimit or 50 if recursionLimit is also omitted.

**Example:**

```yaml filename="endpoints / agents / maxRecursionLimit"
maxRecursionLimit: 100
```

For more information about agent steps, see [Max Agent Steps](/docs/features/agents#max-agent-steps).

## Stream circuit breakers

These settings stop an Agent run when a provider stream appears to be producing runaway tool arguments or an unbounded sequence of delta events.

<OptionTable
  options={[
    [
      'maxToolCallArgBytes',
      'Number',
      'Maximum cumulative bytes for one streamed tool call\'s arguments. Set to 0 to disable the global guard.',
      '65536',
    ],
    [
      'maxDeltaEventsPerTurn',
      'Number',
      'Maximum streamed events for one model generation turn. Set to 0 or omit it to disable this guard.',
      '0 (disabled)',
    ],
    [
      'maxToolCallArgBytesByTool',
      'Object',
      'Per-tool argument-byte overrides keyed by the model-facing tool name. A value of 0 disables the guard for that tool.',
      'create_file: 131072',
    ],
  ]}
/>

LibreChat uses a 64 KiB global argument limit and ships a 128 KiB override for `create_file`, whose legitimate payloads are often larger. Entries in `maxToolCallArgBytesByTool` merge over that built-in override. If either guard trips, LibreChat aborts the in-flight provider stream and reports which limit was exceeded.

```yaml filename="endpoints / agents / stream circuit breakers"
maxToolCallArgBytes: 65536
maxDeltaEventsPerTurn: 100000
maxToolCallArgBytesByTool:
  create_file: 262144
  large_import: 524288
```

To disable every argument-size guard, set both the global limit and the shipped `create_file` override to `0`:

```yaml
maxToolCallArgBytes: 0
maxToolCallArgBytesByTool:
  create_file: 0
```

## titleTiming

<OptionTable
  options={[
    [
      'titleTiming',
      'String',
      'Controls when conversation titles are generated for the agents endpoint. Valid values: "immediate" or "final".',
      'Defaults to "immediate".',
    ],
  ]}
/>

**Default:** `"immediate"`

**Available Values:**

- **`"immediate"`**: Generates the title as soon as the request starts, in parallel with the model response, using the user's first message.
- **`"final"`**: Defers title generation until the full response completes. This preserves the legacy behavior.

**Example:**

```yaml filename="endpoints / agents / titleTiming"
titleTiming: immediate
```

## activityLabel

Set `activityLabel: true` to group each block of Agent reasoning and tool calls under a generated one-line header. Use `activityModel`, `activityEndpoint`, `activityPrompt`, `activityMaxPerRun`, and `activityCharLimit` to control the additional model call and its cost. See [Agent Activity Groups](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#agent-activity-groups) for the full field reference and precedence rules.

```yaml filename="endpoints / agents / activity groups"
activityLabel: true
activityEndpoint: openAI
activityModel: gpt-4.1-nano
activityMaxPerRun: 20
activityCharLimit: 600
activityPhaseLabel: true
activityPhaseMaxPerRun: 5
```

`activityPhaseLabel` can independently add one collapsed parent summary around each run phase that contains at least two logical activities. Use `activityPhaseModel`, `activityPhaseEndpoint`, `activityPhasePrompt`, and `activityPhaseMaxPerRun` to tune those calls; `activityCharLimit` is shared with child activity labels.

## disableBuilder

<OptionTable
  options={[
    [
      'disableBuilder',
      'Boolean',
      'Controls the visibility and use of the builder interface for agents.',
      'When set to `true`, disables the builder interface for the agent, limiting direct manual interaction.',
    ],
  ]}
/>

**Default:** `false`

**Example:**

```yaml filename="endpoints / agents / disableBuilder"
disableBuilder: false
```

## allowedProviders

<OptionTable
  options={[
    [
      'allowedProviders',
      'Array/List of Strings',
      'Specifies a list of endpoint providers (e.g., "openAI", "anthropic", "google") that are permitted for use with the Agents feature.',
      'If defined, only agents configured with these providers can be initialized. If omitted or empty, all configured providers are allowed.',
    ],
  ]}
/>

**Default:** `[]` (empty list, all providers allowed)

**Note:** Must be one of the following, or a custom endpoint name as defined in your [configuration](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#name): - `openAI, azureOpenAI, google, anthropic, assistants, azureAssistants, bedrock`

**Example:**

```yaml filename="endpoints / agents / allowedProviders"
allowedProviders:
  - openAI
  - google
```

## capabilities

<OptionTable
  options={[
    [
      'capabilities',
      'Array/List of Strings',
      'Specifies the agent capabilities available to all users for the agents endpoint.',
      'Defines the agent capabilities that are available to all users for the agents endpoint. You can omit the capabilities you wish to exclude from the list.',
    ],
  ]}
/>

**Default:** `["deferred_tools", "execute_code", "file_search", "web_search", "artifacts", "subagents", "actions", "context", "skills", "memory", "ask_user_question", "tools", "chain", "ocr"]`

`programmatic_tools`, `stateful_code_sessions`, `run_in_background`, and `tool_intents` are opt-in and must be added explicitly. Programmatic tools and stateful sessions also require `execute_code` and a compatible Code Interpreter deployment. The Agent Builder disables Programmatic MCP selections without Code Interpreter, and the server strips stale programmatic caller options when code execution is unavailable. `run_in_background` makes Code Interpreter tools eligible by default and allows per-tool opt-in for eligible MCP, Plugin, and Action tools; background code execution also requires `execute_code` and Code Interpreter.

`stateful_code_sessions` is highly experimental. It requires a separate `stateful`-profile Code Interpreter route configured with [`LIBRECHAT_CODE_BASEURL_STATEFUL`](/docs/configuration/dotenv#stateful-code-interpreter-endpoint) or [`statefulCodeSessions.environments`](#statefulcodesessions). Stateful requests fail closed instead of falling back to the normal stateless service. In the Agent Builder, each enabled Agent chooses a user, agent-and-user, or conversation workspace scope and, when named backends are configured, an execution environment. New Agents start with the user's personal scope default and the deployment's default execution environment.

**Example:**

```yaml filename="endpoints / agents / capabilities"
capabilities:
  - 'deferred_tools'
  # Optional: enables Programmatic Tool Calling for MCP tools marked Programmatic in the Agent Builder.
  # Requires execute_code and a Code Interpreter deployment with the Tool Call Server component.
  # - 'programmatic_tools'
  - 'execute_code'
  - 'file_search'
  - 'web_search'
  - 'artifacts'
  - 'subagents'
  - 'actions'
  - 'context'
  - 'skills'
  - 'memory'
  - 'ask_user_question'
  - 'tools'
  - 'chain'
  - 'ocr'
  # Optional and highly experimental: reuse a user-, agent-and-user-, or
  # conversation-scoped workspace. Requires LIBRECHAT_CODE_BASEURL_STATEFUL
  # or a named statefulCodeSessions environment.
  # - 'stateful_code_sessions'
  # Optional: allow eligible Code Interpreter, MCP, Plugin, and Action tools to run as background tasks.
  # - 'run_in_background'
  # Optional: show live model-written intent labels for native and selected MCP tools.
  # - 'tool_intents'
```

**Note:** This field is optional. If omitted, the default behavior is to include all the capabilities listed in the default.

## statefulCodeSessions

Configures the sharing scopes and optional execution backends for highly experimental stateful Code Interpreter workspaces.

<Callout type="warning" title="Highly experimental">
  Stateful Code Sessions, named execution environments, attached workers, and pairing are in an
  early experimentation phase. Their behavior, configuration, persistence characteristics, and
  integration protocol may change substantially.
</Callout>

<OptionTable
  options={[
    [
      'allowedEnvironments',
      'Array/List of Strings',
      'Allowed stateful workspace scopes: `user`, `agent-user`, and/or `conversation`.',
      '["user", "agent-user", "conversation"]',
    ],
    [
      'environments',
      'Array of Objects',
      'Optional named managed or attached Code API environments shown in the Agent Builder.',
      'Unset; LIBRECHAT_CODE_BASEURL_STATEFUL remains the stateful endpoint',
    ],
  ]}
/>

```yaml filename="endpoints / agents / statefulCodeSessions"
statefulCodeSessions:
  allowedEnvironments:
    - 'user'
    - 'agent-user'
    - 'conversation'
  environments:
    - id: managed-default
      name: Managed Code API
      type: managed
      baseURL: https://code.example.com/v1
      default: true
    - id: engineering-vm
      name: Engineering VM
      type: attached
      baseURL: https://code-bridge.example.com/v1
      owner: deployment
      pairing:
        workerId: engineering-vm
        tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
    - id: personal-workers
      name: Personal Code Workers
      type: attached
      baseURL: https://code-bridge.example.com/v1
      owner: deployment
      configSchema:
        permissions:
          fileWrite:
            allowed: [allow, ask, deny]
            default: ask
          commandExecution:
            allowed: [ask, deny]
            default: ask
      pairing:
        allowPrincipalWorkers: true
        tokenEnv: CODE_BRIDGE_ADMIN_TOKEN
```

`allowedEnvironments` is required whenever the block is present and must include at least one workspace scope. Omit the entire block to allow all three scopes and continue routing stateful work through [`LIBRECHAT_CODE_BASEURL_STATEFUL`](/docs/configuration/dotenv#stateful-code-interpreter-endpoint). The personal default and Agent Builder only permit allowed values. If an administrator tightens the policy, a saved personal default remains visible but disabled, and new Agents use the first allowed scope. Existing enabled Agents retain their saved scope, but a now-disallowed scope fails closed at runtime until the Agent is reconfigured.

When `environments` is non-empty, every entry requires a unique `id`, a display `name`, a `type` of `managed` or `attached`, and an HTTP(S) `baseURL` without a query string or fragment. If the list contains executable environments, exactly one of those entries must set `default: true`. The Agent Builder then adds an **Execution environment** selector; leaving it on **Deployment default** uses that default entry. A list containing only self-service pairing control planes needs no default. An Agent that names an environment that is later removed or no longer accessible fails closed instead of falling back to another backend.

- `managed` points directly to an operator-managed stateful Code API.
- `attached` points to a compatible Code API `remote-bridge` backend that leases work to an outbound `@librechat/code` worker. The worker does not require an inbound public port, but its local execution endpoint still needs appropriate isolation.
- `workerId` optionally routes an attached environment to one outbound worker. It is server-only and is not exposed through client startup configuration.
- `owner` defaults to `deployment`. Principal-owned environments are created through LibreChat's authenticated Code Environments API, protected by ACLs, and merged only for authorized requests; do not place client-supplied URLs in deployment YAML.
- `pairing` is optional and valid only for deployment-owned `attached` entries. `pairing.workerId` identifies an operator-managed worker to enroll, while `pairing.allowPrincipalWorkers: true` lets authorized users enroll owner-bound workers against that control plane. At least one of those fields is required when `pairing` is present.
- `pairing.tokenEnv` names the environment variable containing the bridge administrator token; it never contains the token itself. Pairing requires HTTPS except for loopback development.
- `configSchema.permissions` optionally lets authorized owners choose `fileWrite` and `commandExecution` policy for personal environments under **Settings > Code environments**. Each field requires a non-empty `allowed` list containing `allow`, `ask`, and/or `deny`; `default` is `ask` when omitted and must appear in `allowed`. LibreChat rejects stored values outside the administrator's list.
- Do not configure `settings` in deployment YAML. LibreChat supplies that request-scoped field from the authenticated owner's server-validated preferences.
- Only file-write and command-execution policy is user-configurable. Isolation, networking, mounts, privileged execution, ingress, egress, and secrets remain administrator-controlled.
- If both top-level `workerId` and `pairing.workerId` are present, they must match. An entry with `allowPrincipalWorkers: true` and neither worker ID is pairing-only: it is omitted from execution selectors and cannot set `default: true`.

Attached execution automatically enables a safe approval baseline: file writes and command or code execution ask for confirmation, while read-only and search operations continue under the regular tool policy. The baseline is scoped to the Agent using the attached environment. If no user setting is available, LibreChat uses `ask`; [`toolApproval.enabled: false`](#toolapproval) is the administrator emergency override that disables this attached-environment baseline. Callers without approval and resume support fail closed unless that explicit override is set.

Attached execution requires a matching experimental Code Interpreter remote-bridge build. Pairing authenticates the outbound worker connection; it does not replace sandbox or VM isolation. Users with Code Environment management permission pair, configure permitted tool policy, and revoke personal workers under **Settings > Code environments**. See [Stateful Code Sessions](/docs/features/code_interpreter#stateful-code-sessions).

## eventDriven

Configures the trusted internal origin used by Agent event delivery. Most deployments should omit this block and use the current process's bound listener.

<OptionTable
  options={[
    [
      'selfUrl',
      'URL',
      'Overrides the current process bound listener for trusted internal trigger admission. Use only when delivery must traverse another trusted HTTP origin.',
      '',
    ],
  ]}
/>

```yaml filename="endpoints / agents / eventDriven"
endpoints:
  agents:
    eventDriven:
      selfUrl: https://librechat.internal
```

Bound child continuations are automatic. Older `childTurns`, `completionWakeups`, `coalescing`, `actorMailbox`, `checkpointForks`, and `durableReceipts` fields under `eventDriven` are no longer configuration switches and should be removed. The runtime now applies those delivery guarantees directly. The separate [`backgroundTasks.completionWakeups`](#backgroundtasks) field controls conversational delivery for completed background tools and detached Subagents.

Leave `selfUrl` unset for the normal bound-listener path. Set it only when trusted internal trigger admission must traverse another HTTP(S) origin, such as a TLS front door. `AGENT_TRIGGERS_SELF_URL` remains a compatibility fallback when this field is omitted. See [Agents API events](/docs/features/agents_api#agent-events) and [Automatic Parent Continuation](/docs/features/subagents#automatic-parent-continuation).

Event Actor detached Action completion is selected automatically from the built-in generation store. In-memory execution is process-local; Redis generation streams add durable restart recovery and replica handoff. No `librechat.yaml` switch or environment feature flag is required. See [Agent Event Runtime](/docs/configuration/dotenv#agent-event-runtime).

## backgroundTasks

Controls whether supported completed background tools and detached Subagents automatically resume their saved parent Agent.

<OptionTable
  options={[
    [
      'completionWakeups',
      'Boolean',
      'Automatically deliver supported background-task completions as a continuation. Set to false for poll-only behavior.',
      'true',
    ],
  ]}
/>

```yaml filename="endpoints / agents / backgroundTasks"
backgroundTasks:
  completionWakeups: true
```

Automatic completion delivery is enabled even when this block is omitted. Set `completionWakeups: false` to require the Agent to collect every result through `check_background_task`.

Ordinary background tool execution remains process-local and does not survive the loss of its worker process. Once a content-only terminal result is persisted, its delivery is durable and may continue on another replica. Tasks with live artifacts still require polling on the owning run. Detached Subagents keep their separate durable transcript and terminal-result path. Manual polling remains available for status, controls, and recovery in either mode. See [Background Tool Calls](/docs/features/agents#background-tool-calls) and [Automatic Parent Continuation](/docs/features/subagents#automatic-parent-continuation).

## toolApproval

Controls human review for Agent tool calls. The deployment-wide policy is disabled when this block is omitted. An Agent using an attached Code environment is the exception: LibreChat enables its scoped ask-by-default safety policy unless the administrator explicitly sets `enabled: false`. When review applies, matching calls pause until the user submits an allowed decision, such as approving, rejecting, or editing the arguments.

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Enables deployment-wide tool approval. Explicit false also disables the attached Code environment safety baseline.',
      'Omitted',
    ],
    [
      'mode',
      'String',
      'Sets unmatched-call behavior: `default` asks, `dontAsk` denies, and `bypass` approves unless denied.',
      'default',
    ],
    ['allow', 'Array of strings', 'Glob patterns for calls that can run without prompting.', ''],
    ['deny', 'Array of strings', 'Glob patterns for calls that are always denied. Deny rules win.', ''],
    ['ask', 'Array of strings', 'Glob patterns for calls that always require review.', ''],
    [
      'reason',
      'String',
      'Optional explanation shown in the approval prompt. Use `{tool}` to insert the tool name.',
      '',
    ],
    [
      'hooks',
      'Array of objects',
      'Trusted programmatic policy modules for context-aware decisions. Hooks can only tighten the static policy.',
      '',
    ],
  ]}
/>

Patterns use glob matching. MCP tools can be scoped with `mcp:<server>:<tool>`; for example, `mcp:github:*` matches every tool from the `github` server. LibreChat applies static rules and hook matchers to MCP runtime names and model-facing aliases, including tools reachable through nested Subagents. Static rules are evaluated in `deny`, `ask`, then `allow` order before the selected mode supplies the fallback. A deny rule therefore always wins, including in `bypass` mode.

```yaml filename="endpoints / agents / toolApproval"
toolApproval:
  enabled: true
  mode: default
  allow:
    - 'mcp:analytics:read_*'
  deny:
    - 'mcp:*:delete_*'
  ask:
    - 'mcp:*:*'
  reason: 'Review {tool} before it runs.'
```

Hooks are loaded once at server startup and execute in the LibreChat process. Each entry accepts a `module`, an optional tool-name `matcher` regex, and an optional `options` object passed to the module's builder. Only load code you trust. Per-role or tenant overrides do not reload hook modules; implement contextual behavior inside the hook.

```yaml filename="endpoints / agents / toolApproval / hooks"
toolApproval:
  enabled: true
  hooks:
    - module: '@acme/librechat-approval-hook'
      matcher: '^mcp:production:'
      options:
        requireTicket: true
```

## checkpointer

Configures storage for Agent runs paused by tool approval or Ask User. When either feature needs a checkpointer and this block is omitted, LibreChat uses the primary MongoDB with a 24-hour decision window.

<OptionTable
  options={[
    [
      'type',
      'String',
      '`mongo` persists paused runs across restarts and replicas; `memory` is process-local and intended for development.',
      'mongo',
    ],
    ['ttl', 'Number', 'How long a paused run waits for a decision, in seconds.', '86400'],
    [
      'checkpointCollectionName',
      'String',
      'Optional MongoDB checkpoint collection name override.',
      'agent_checkpoints',
    ],
    [
      'checkpointWritesCollectionName',
      'String',
      'Optional MongoDB checkpoint-write collection name override.',
      'agent_checkpoint_writes',
    ],
  ]}
/>

```yaml filename="endpoints / agents / checkpointer"
checkpointer:
  type: mongo
  ttl: 86400
```

Use `type: memory` only for a single-process development deployment. A paused run stored in memory cannot resume after a restart or on another replica.

MongoDB checkpoints log a warning when serialized state exceeds 8 MiB and reject a durable pause above 15 MiB, leaving headroom below MongoDB's 16 MiB document limit. Large inlined media or tool outputs are common causes; start a new conversation or reduce its context if a run reports `CHECKPOINT_TOO_LARGE`.

## skills

Controls endpoint-level Skills settings for agents.

<OptionTable
  options={[
    [
      'skills.maxCatalogSkills',
      'Number',
      'Caps the number of active accessible Skills exposed in the model-visible catalog. Must be between 1 and 100.',
      'maxCatalogSkills: 20',
    ],
  ]}
/>

**Default:** No configured cap beyond the runtime catalog limit.

**Example:**

```yaml filename="endpoints / agents / skills"
skills:
  maxCatalogSkills: 20
```

This does not disable Skills. Use the `skills` capability and per-agent/model-spec skill scoping to control whether Skills are available.

## maxSubagents

<OptionTable
  options={[
    [
      'maxSubagents',
      'Number',
      'Maximum number of explicit subagents a single agent may reference.',
      'Applies to the `agent_ids` allowlist on agents and on model specs, and to configured subagent `graphs`, which agents support but model specs do not. Must be a whole number between 1 and 50.',
    ],
  ]}
/>

**Default:** `10`

**Range:** `1-50`

**Example:**

```yaml filename="endpoints / agents / maxSubagents"
endpoints:
  agents:
    maxSubagents: 20
```

Raise this when a deployment runs orchestration-heavy agents that need to delegate to more than ten children. The value is read at startup and enforced in three places:

- Agent create, update, and duplicate requests, for `subagents.agent_ids` and `subagents.graphs`
- Model spec `subagents.agent_ids` allowlists, validated in the same configuration pass
- The Agent Builder panel, which stops accepting new subagent entries at the configured value

Model specs accept only `enabled`, `allowSelf`, and `agent_ids` under `subagents`. Subagent `graphs` are an agent-level feature: a `graphs` key on a model spec is dropped when the config is parsed, without an error.

<Callout type="warning">
  `maxSubagents` must be a whole number between 1 and 50. A value outside that range fails
  `librechat.yaml` validation, and LibreChat logs the error and exits at startup instead of falling
  back to the default. Starting with `CONFIG_BYPASS_VALIDATION=true` skips the exit, but the entire
  custom config is discarded in that case, so the cap returns to `10`. To go back to the default
  deliberately, remove the key: an omitted `maxSubagents` resolves to `10` with no error.
</Callout>

<Callout type="note">
  The cap is process-wide and comes from `librechat.yaml`. A per-user or per-group database override
  of `endpoints.agents.maxSubagents` is reflected in the served configuration but is not applied by
  request validation.
</Callout>

Raising this only changes how many subagents one agent may reference. The depth, graph node, and run
configuration limits are unaffected. See [Subagents](/docs/features/subagents#limits).

## maxCitations

<OptionTable
  options={[
    [
      'maxCitations',
      'Number',
      'Controls the maximum total number of citations that can be included in a single agent response.',
      'When using file_search capability, limits the total number of source citations returned to prevent overwhelming responses while ensuring comprehensive coverage.',
    ],
  ]}
/>

**Default:** `30`

**Range:** `1-50`

**Example:**

```yaml filename="endpoints / agents / maxCitations"
maxCitations: 30
```

## maxCitationsPerFile

<OptionTable
  options={[
    [
      'maxCitationsPerFile',
      'Number',
      'Limits the maximum number of citations that can be extracted from any single file.',
      'Ensures citation diversity by preventing any single file from dominating the citations, encouraging representation from multiple sources.',
    ],
  ]}
/>

**Default:** `7`

**Range:** `1-10`

**Example:**

```yaml filename="endpoints / agents / maxCitationsPerFile"
maxCitationsPerFile: 7
```

## minRelevanceScore

<OptionTable
  options={[
    [
      'minRelevanceScore',
      'Number',
      'Sets the minimum relevance score threshold for sources to be included in responses.',
      'Filters out low-quality matches based on vector similarity scores. Higher values (e.g., 0.7) ensure only highly relevant sources are cited, while lower values (e.g., 0.0) include all sources regardless of quality.',
    ],
  ]}
/>

**Default:** `0.45` (45% relevance threshold)

**Range:** `0.0-1.0`

**Example:**

```yaml filename="endpoints / agents / minRelevanceScore"
minRelevanceScore: 0.45
```

### File Citation Configuration Examples

**Default Configuration (Balanced)**

```yaml
endpoints:
  agents:
    maxCitations: 30
    maxCitationsPerFile: 7
    minRelevanceScore: 0.45
```

Provides comprehensive citations while preventing overwhelming responses and filtering out low-quality matches.

**Strict Configuration (High Quality)**

```yaml
endpoints:
  agents:
    maxCitations: 10
    maxCitationsPerFile: 3
    minRelevanceScore: 0.7
```

Only includes highly relevant citations with strict limits for focused responses.

**Comprehensive Configuration (Research)**

```yaml
endpoints:
  agents:
    maxCitations: 50
    maxCitationsPerFile: 10
    minRelevanceScore: 0.0
```

Maximum information extraction for exhaustive research tasks, including all sources regardless of relevance.

## Agent Capabilities

The `capabilities` field allows you to enable or disable specific functionalities for agents. The available capabilities are:

- **deferred_tools**: Allows agents to discover deferred MCP tools at runtime instead of loading every tool into context upfront.
- **programmatic_tools**: Enables Programmatic Tool Calling for MCP tools marked Programmatic in the Agent Builder. Requires `execute_code` and a Code Interpreter deployment with the Tool Call Server component. This capability is opt-in and is not enabled by default.
- **execute_code**: Allows the agent to execute code.
- **stateful_code_sessions**: Lets opted-in agents reuse one Code Interpreter workspace at a user, agent-and-user, or conversation scope. Requires `execute_code`, a compatible Code Interpreter deployment, and the per-agent Advanced setting. New Agents use the signed-in user's preferred scope. This capability is highly experimental, is not enabled by default, and may change substantially during experimentation; do not treat its current behavior or configuration as a stable production contract.
- **file_search**: Enables the agent to search and interact with files. When enabled, citation behavior is controlled by `maxCitations`, `maxCitationsPerFile`, and `minRelevanceScore` settings.
- **web_search**: Enables web search functionality for agents, allowing them to search and retrieve information from the internet.
- **artifacts**: Enables the agent to generate interactive artifacts (React components, HTML, Mermaid diagrams).
- **subagents**: Enables isolated-context child agent runs. See [Subagents](/docs/features/subagents).
- **actions**: Permits the agent to perform predefined actions.
- **context**: Enables "Upload as Text" functionality in chat, and "File Context" for agents, allowing users to upload files and have their content extracted and included directly in the conversation.
- **skills**: Enables Skills in the side panel, manual `$` invocation, model-invoked skills, and agent skill allowlists. See [Skills](/docs/features/skills).
- **memory**: Lets agents use `set_memory` and `delete_memory` when memory is configured and the user has access. Enabled by default; removed at runtime when memory is disabled.
- **ask_user_question**: Lets agents pause to ask one to four related questions in a single form and resume with the answers. Enabled by default.
- **tools**: Grants the agent access to various tools.
- **chain**: Enables Beta feature for agent chaining, also known as Mixture-of-Agents (MoA) workflows.
- **ocr**: Optionally enhances "Upload as Text" in chat, and "File Context" for agents, allowing files to be uploaded and processed with OCR. **Requires an OCR service to be configured.**
- **run_in_background**: Makes Code Interpreter execution and shell tools background-eligible by default and enables per-tool background opt-in for eligible MCP, Plugin, and Action tools. An Action-level switch opts in every eligible operation; OAuth Actions and operations that already define a `run_in_background` parameter are excluded. An agent can explicitly opt Code Interpreter out. The setting permits background execution; the model decides per call whether to use it. Requires `execute_code` and a configured Code Interpreter deployment for code. This capability is not enabled by default.
- **tool_intents**: Adds live model-written intent labels to eligible tool calls. Native tools, including Code Interpreter tools, opt in automatically; MCP tools can be enabled individually or in bulk in the Agent Builder. Model specs use `describeIntent`. This capability is not enabled by default.

By specifying the capabilities, you can control the features available to users when interacting with agents.

## Example Configuration

Here is an example of configuring the `agents` endpoint with custom capabilities and file citation settings:

```yaml filename="Agents Endpoint"
endpoints:
  agents:
    disableBuilder: false
    # File citation configuration
    maxCitations: 20
    maxCitationsPerFile: 5
    minRelevanceScore: 0.6
    # Custom capabilities
    capabilities:
      # Optional: enables Programmatic Tool Calling for MCP tools marked Programmatic in the Agent Builder.
      # - 'programmatic_tools'
      - 'execute_code'
      # Optional: makes Code Interpreter background-eligible and enables selected MCP, Plugin, and Action tools.
      # - 'run_in_background'
      # Optional: enables live intent labels for native and selected MCP tools.
      # - 'tool_intents'
      - 'file_search'
      - 'skills'
      - 'subagents'
      - 'actions'
      - 'artifacts'
      - 'context'
      - 'ocr'
      - 'web_search'
```

In this example:

- The builder interface is enabled
- File citations are limited to 20 total, with maximum 5 per file
- Only sources with 60%+ relevance are included
- LibreChat Agents have access to code execution, file search (with citations), Skills, Subagents, actions, artifacts, file context, ocr services if configured, and web search capabilities
- Programmatic Tool Calling remains disabled unless you add the `programmatic_tools` capability alongside `execute_code`
- Background tool calls remain disabled unless you add `run_in_background`; Code Interpreter tools then become eligible by default, while eligible MCP, Plugin, and Action tools still require selection
- Tool intent labels remain disabled unless you add `tool_intents`; native tools then opt in automatically, while MCP tools still require per-tool selection

## remoteApi

Configuration for Remote Agent API authentication. Controls how external services authenticate when calling the Agents API endpoints.

### remoteApi.auth

<OptionTable
  options={[
    [
      'auth',
      'Object',
      'Authentication configuration for the Remote Agent API.',
      'Supports API key and/or OIDC Bearer token authentication. If omitted, only API key auth is active.',
    ],
  ]}
/>

#### remoteApi.auth.apiKey

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Enable API key authentication for the Remote Agent API.',
      'When true, requests with a valid LibreChat API key are accepted. Can be used alongside or instead of OIDC.',
    ],
  ]}
/>

**Default:** `true`

#### remoteApi.auth.oidc

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Enable OIDC Bearer token authentication.',
      'When true, the middleware validates Bearer tokens against the configured OIDC issuer via JWKS.',
    ],
    [
      'issuer',
      'String',
      'OIDC issuer URL.',
      'The base URL of your OIDC provider, such as a Keycloak realm URL. Used for token issuer validation and JWKS discovery if jwksUri is not set.',
    ],
    [
      'jwksUri',
      'String',
      'JWKS endpoint URL. Optional.',
      'If omitted, resolved automatically via {issuer}/.well-known/openid-configuration. You can also set OPENID_JWKS_URL as an alternative.',
    ],
    [
      'audience',
      'String',
      'Expected token audience. Required when OIDC auth is enabled.',
      'Tokens must contain this value in their aud claim.',
    ],
    [
      'scope',
      'String',
      'Required scope value. Optional.',
      'If set, the token must contain this value in its scp or scope claim. Use this to distinguish token intent across different APIs.',
    ],
  ]}
/>

**Default:** `enabled: false`

**Example - OIDC only:**

```yaml filename="endpoints / agents / remoteApi"
endpoints:
  agents:
    remoteApi:
      auth:
        apiKey:
          enabled: false
        oidc:
          enabled: true
          issuer: https://auth.example.com/realms/myrealm
          audience: my-client-id
```

**Example - OIDC with API key fallback:**

```yaml filename="endpoints / agents / remoteApi"
endpoints:
  agents:
    remoteApi:
      auth:
        apiKey:
          enabled: true
        oidc:
          enabled: true
          issuer: https://auth.example.com/realms/myrealm
          # jwksUri is optional and auto-discovered if omitted
          jwksUri: https://auth.example.com/realms/myrealm/protocol/openid-connect/certs
          audience: my-client-id
```

<Callout type="info">
  JWKS URI resolution priority is explicit `jwksUri`, then `OPENID_JWKS_URL`, then
  auto-discovery via `{issuer}/.well-known/openid-configuration`.
</Callout>

<Callout type="info">
  OIDC user matching uses the `sub` claim as primary lookup, with fallback to `email`,
  `preferred_username`, or `upn` claims. The matched user must already exist in LibreChat.
</Callout>

## Subagents

The `subagents` field controls which isolated child agents a parent agent can spawn when the `subagents` capability is available.

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Adds the subagent spawn tool to this agent when true. Default: disabled.',
      'enabled: true',
    ],
    [
      'allowSelf',
      'Boolean',
      'Allows the agent to spawn itself in a fresh isolated context. Default: true.',
      'allowSelf: true',
    ],
    [
      'agent_ids',
      'Array/List of Strings',
      'Specific agents this agent may spawn. Capped by `maxSubagents`, which defaults to 10.',
      'agent_ids: ["agent_researcher"]',
    ],
    [
      'graphs',
      'Array of Objects',
      'Saved Agent teams that can run as one isolated child graph. The list is capped by `maxSubagents` (default: 10); each team can contain up to 32 members.',
      '',
    ],
  ]}
/>

```yaml filename="Agent subagents"
subagents:
  enabled: true
  allowSelf: true
  agent_ids:
    - 'agent_researcher'
    - 'agent_reviewer'
```

Each `graphs` entry requires a unique `type`, `name`, `description`, `agent_ids`, direct `edges`, `entry_agent_id`, and `result_agent_id`. Team definitions must form a connected directed acyclic graph, every member must be visible to the user, and the total configuration is bounded to 50 unique Agent targets and 100 expanded run configurations. See the [saved-team example and runtime behavior](/docs/features/subagents#configure-an-agent).

For user-facing behavior and limits, see [Subagents](/docs/features/subagents).

## Notes

- It's not recommended to disable the builder interface unless you are using [modelSpecs](/docs/configuration/librechat_yaml/object_structure/model_specs) to define a list of agents to choose from.
- File citation configuration (`maxCitations`, `maxCitationsPerFile`, `minRelevanceScore`) only applies when the `file_search` capability is enabled.
- The relevance score is calculated using vector similarity, where 1.0 represents a perfect match and 0.0 represents no similarity.
- Citation limits help balance comprehensive information retrieval with response quality and performance.
- The `context` capability works without OCR configuration using text parsing methods. OCR enhances extraction quality when configured.
- The `ocr` capability requires an OCR service to be configured (see [OCR Configuration](/docs/configuration/librechat_yaml/object_structure/ocr)).
- `maxSubagents` bounds how many subagents a single agent may reference. It does not change the depth or graph limits documented under [Subagents](/docs/features/subagents#limits).


# Actions Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/actions)

Actions can be used to dynamically create tools from OpenAPI specs. The `actions` object structure allows you to specify allowed domains for agent/assistant actions.

More info: [Agents - Actions](/docs/features/agents#actions)

## Example

```yaml filename="Actions Object Structure"
# Example Actions Object Structure
actions:
  # Strict whitelist mode:
  # allowedDomains:
  #   - "swapi.dev"
  #   - "librechat.ai"
  #   - "google.com"
  #   - "https://api.example.com:8443"  # With protocol and port

  # Default SSRF mode with private service exemptions:
  allowedAddresses:
    - "host.docker.internal:11434"    # Permit one private host on one port
    - "10.0.0.5:8080"                 # Permit one private IP on one port
```

## allowedDomains

**Key:**
<OptionTable
  options={[
    ['allowedDomains', 'Array of Strings', 'A list specifying allowed domains for agent/assistant actions.', 'When configured, only listed domains are allowed. When not configured, SSRF targets are blocked but all other domains are allowed.'],
  ]}
/>

**Optional**

### Security Context (SSRF Protection)

LibreChat includes SSRF (Server-Side Request Forgery) protection with the following behavior:

When an Action is created from OpenAPI metadata, LibreChat also verifies that the submitted Action domain matches the spec's server URL. If the submitted domain includes an explicit port, it must match the server URL's effective port (`443` for HTTPS or `80` for HTTP when the URL omits one).

**When `allowedDomains` is NOT configured:**
- SSRF-prone targets are **blocked by default**
- All other external domains are **allowed**

**When `allowedDomains` IS configured:**
- **Only** domains on the list are allowed
- Internal/SSRF targets can be allowed by explicitly adding them to the list

**Blocked SSRF targets include:**
- **Localhost** addresses (`localhost`, `127.0.0.1`, `::1`)
- **Private IP ranges** (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`)
- **Link-local addresses** (`169.254.0.0/16`, includes cloud metadata IPs)
- **Internal TLDs** (`.internal`, `.local`, `.localhost`)
- **Common internal service names** (`redis`, `mongodb`, `postgres`, `api`, etc.)

If your actions need to access internal services, either add them to the strict `allowedDomains` whitelist, or leave `allowedDomains` unset and add the exact private service to `allowedAddresses`.

### Pattern Formats

The `allowedDomains` array supports several formats:

1. **Domain only** - Allows all protocols and ports:
   ```yaml
   allowedDomains:
     - "api.example.com"
   ```

2. **With protocol** - Restricts to specific protocol:
   ```yaml
   allowedDomains:
     - "https://api.example.com"
   ```

3. **With protocol and port** - Restricts to specific protocol and port:
   ```yaml
   allowedDomains:
     - "https://api.example.com:8443"
   ```

4. **Internal addresses** (must be explicitly allowed):
   ```yaml
   allowedDomains:
     - "192.168.1.100"
     - "internal-api.local"
   ```

**Example:**
```yaml filename="actions / allowedDomains"
allowedDomains:
  - "swapi.dev"
  - "librechat.ai"
  - "google.com"
  - "https://secure-api.example.com:443"
  - "192.168.1.50"  # Internal service (explicitly allowed)
```

## allowedAddresses

`allowedAddresses` is an **exemption list** for the SSRF private-IP block — not a domain whitelist. It is the right tool when you want to permit one or two specific private/internal services without restricting what your Actions can reach in the public internet.

### When to use it instead of `allowedDomains`

`allowedDomains` is a strict whitelist: when it is set, **only** listed entries are reachable. Adding a private IP there to permit, say, a self-hosted internal API also blocks every public action endpoint that you didn't also list.

`allowedAddresses` is used only when `allowedDomains` is not configured. It permits specific private `host:port` targets while leaving the rest of the public internet reachable through the default SSRF policy.

```yaml filename="default SSRF + permitted private host"
actions:
  allowedAddresses:
    - "host.docker.internal:11434"
    - "10.0.0.5:8080"
  # allowedDomains is intentionally not set — public destinations
  # remain reachable, only listed private host:port services are exempted.
```

If `allowedDomains` is configured, it is authoritative: private services must be listed there instead of relying on `allowedAddresses`.

### Acceptable entries

- **Hostnames with port**: `host.docker.internal:11434`, `ollama.internal:8080`, `localhost:11434`
- **Private IPv4 literals with port**: `10.0.0.5:8080`, `127.0.0.1:11434`, `192.168.1.10:443`, `169.254.169.254:80`
- **Bracketed private IPv6 literals with port**: `[::1]:11434`, `[fc00::1]:8080`, `[fe80::1]:8080`

### Rejected entries (validated at config load)

- **URLs / paths / CIDR ranges**: `http://10.0.0.5`, `10.0.0.0/24`, `/path`
- **Bare hostnames or IPs**: `localhost`, `10.0.0.5`, `::1`, `[::1]` — every entry must include a port
- **Invalid ports**: `localhost:0`, `localhost:65536`, `localhost:http`
- **Public IP literals**: `8.8.8.8:53`, `1.1.1.1:53`, `[2001:4860::8888]:443` — the field is scoped to private IP space; public IPs are not SSRF targets and a public-IP exemption has no defensive purpose

### Hostname trust

A hostname entry trusts whatever IP that hostname resolves to at runtime on the listed port. If the DNS for a listed hostname is rotated or hijacked to point at a different private IP, the exemption follows. Only list hostnames whose DNS you control. **Prefer literal IPs when you can.**


# MCP Servers Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/mcp_servers)

## Example

```yaml filename="MCP Servers Object Structure"
# Example MCP Servers Object Structure
mcpServers:
  everything:
    # type: sse # type can optionally be omitted
    url: http://localhost:3001/sse
  googlesheets:
    type: sse
    url: https://mcp.composio.dev/googlesheets/some-endpoint
    requiresOAuth: true
    headers:
      X-User-ID: '{{LIBRECHAT_USER_ID}}'
      X-API-Key: '${SOME_API_KEY}'
    serverInstructions: true # Use server-provided instructions
  puppeteer:
    type: stdio
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-puppeteer'
    serverInstructions: 'Do not access any local files or local/internal IP addresses'
  filesystem:
    # type: stdio
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-filesystem'
      - /home/user/LibreChat/
    iconPath: /home/user/LibreChat/client/public/assets/logo.svg
    # The “wrench” icon shows up if no icon is provided as it is the default rendering.
  mcp-obsidian:
    command: npx
    args:
      - -y
      - 'mcp-obsidian'
      - /path/to/obsidian/vault
  streamable-http-server:
    type: streamable-http
    url: https://example.com/api/
    proxy: '${MCP_PROXY_URL}'
  per-user-credentials-example:
    type: streamable-http
    url: 'https://example.com/api/'
    headers:
      X-Auth-Token: '{{MY_SERVICE_API_KEY}}'
    customUserVars:
      MY_SERVICE_API_KEY:
        title: 'My Service API Key'
        description: "Enter your personal API key for the service. You can generate one at <a href='https://myservice.example.com/developer/keys' target='_blank'>Service Developer Portal</a>."
        sensitive: true
      MY_SERVICE_PROJECT:
        title: 'Project ID'
        description: 'Enter the project ID used by this service.'
        sensitive: false
  oauth-example:
    type: streamable-http
    url: https://api.example.com/mcp/
    oauth:
      authorization_url: https://example.com/oauth/authorize
      token_url: https://example.com/oauth/token
      client_id: your_client_id
      client_secret: your_client_secret
      redirect_uri: http://localhost:3080/api/mcp/oauth-example/oauth/callback
      scope: 'read execute'
  obo-example:
    type: streamable-http
    url: https://api.example.com/mcp/
    obo:
      scopes: 'api://mcp-server-id/Mcp.Tools.ReadWrite'
```

<Callout type="warning" title="Process-backed servers are operator-owned">
  MCP servers that use `type: stdio` or process fields such as `command`, `args`, `env`, `cwd`, or `stderr` must be configured in `librechat.yaml`. The admin configuration API rejects process-backed entries, and database or per-principal overrides cannot replace or remove an existing process-backed server. Remote MCP servers continue to follow the normal configuration-override precedence.
</Callout>

## `<serverName>`

**Key:**

<OptionTable
  options={[
    [
      '<serverName>',
      'Object',
      'Each key under `mcpServers` represents an individual MCP server configuration, identified by a unique name. This name is used to reference the server configuration within the application.',
      '',
    ],
  ]}
/>

<Callout type="warning" title="Use normalization-safe unique names">
  Model-facing tool keys preserve letters, numbers, `_`, `.`, and `-`; other characters in a server
  name are replaced with `_`. LibreChat maps those normalized keys back to the configured name and
  heals older saved Agent tool references. Avoid two names that normalize to the same value, such as
  `Sales Force` and `Sales:Force`: their tool keys are ambiguous, so LibreChat warns and excludes the
  shadowed server's tools rather than risk routing a call to the wrong server.
</Callout>

### Subkeys

<OptionTable
  options={[
    [
      'title',
      'String',
      '(Optional) Custom display name for the MCP server in the UI. If not specified, the server key name is used.',
      'title: "My Custom Server"',
    ],
    [
      'description',
      'String',
      '(Optional) Description of the MCP server, displayed in the UI to help users understand its purpose.',
      'description: "Provides file system access"',
    ],
    [
      'type',
      'String',
      'Specifies the connection type to the MCP server. Valid options are `"stdio"`, `"websocket"`, `"streamable-http"`, or `"sse"`. If omitted, it defaults based on the presence and format of `url` or `command`.',
      'type: "stdio"',
    ],
    [
      'command',
      'String',
      '(For `stdio` type) The command or executable to run to start the MCP server.',
      'command: "npx"',
    ],
    [
      'args',
      'Array of Strings',
      '(For `stdio` type) Command line arguments to pass to the `command`.',
      'args: ["-y", "@modelcontextprotocol/server-puppeteer"]',
    ],
    [
      'url',
      'String',
      '(For `websocket`, `streamable-http`, or `sse` type) The URL to connect to the MCP server.',
      'url: "http://localhost:3001/sse"',
    ],
    [
      'proxy',
      'String',
      '(Optional, for `sse` and `streamable-http` types) Outbound proxy URL for this remote MCP server. Supports `http://`, `https://`, `socks://`, and `socks5://` URLs.',
      'proxy: "${MCP_PROXY_URL}"',
    ],
    [
      'headers',
      'Object',
      '(Optional, for `sse` and `streamable-http` types) Custom headers to send with the request. Supports dynamic user field substitution with `{{LIBRECHAT_USER_*}}` placeholders and environment variables with `${ENV_VAR}`.',
      'headers:\n  X-User-ID: "{{LIBRECHAT_USER_ID}}"\n  X-API-Key: "${SOME_API_KEY}"',
    ],
    [
      'apiKey',
      'Object',
      '(Optional, for `sse` and `streamable-http` types) API key authentication configuration for the MCP server.',
      'See apiKey section below',
    ],
    [
      'iconPath',
      'String',
      "(Optional) Defines the tool's display icon shown in the tool selection dialog.",
      'iconPath: "/path/to/icon.svg"',
    ],
    [
      'chatMenu',
      'Boolean',
      '(Optional) When `false`, excludes the MCP server from the regular chat picker and rejects it from picker-supplied chat selections. Saved Agents and explicitly assigned model specs can still use it. Defaults to `true`.',
      'chatMenu: false',
    ],
    [
      'serverInstructions',
      'Boolean or String',
      '(Optional) Controls how MCP server instructions are injected into agent context. Server instructions provide high-level usage guidance for the entire MCP server, complementing individual tool descriptions.',
      'serverInstructions: true\n# or\nserverInstructions: "Custom instructions"',
    ],
    [
      'timeout',
      'Integer',
      '(Optional) Timeout in milliseconds for MCP server requests. Must be a non-negative integer.',
      'timeout: 30000',
    ],
    [
      'initTimeout',
      'Integer',
      '(Optional) Timeout in milliseconds for MCP server initialization. Must be a non-negative integer.',
      'initTimeout: 10000',
    ],
    [
      'env',
      'Object',
      '(Optional, `stdio` type only) Environment variables to use when spawning the process.',
      'env:\n  NODE_ENV: "production"',
    ],
    [
      'requiresOAuth',
      'Boolean',
      "(Optional, remote transports: `sse`, `streamable-http`, `websocket`) Whether this server requires OAuth authentication. If not specified, will be auto-detected during server startup. Although optional, it's best to set this value explicitly if you know whether the server requires OAuth or not. Setting `requiresOAuth: false` is useful for servers protected by a static `Authorization` header, to skip auto-detection that would otherwise misclassify them as OAuth-protected.",
      'requiresOAuth: false',
    ],
    [
      'stderr',
      'String or Integer',
      '(Optional, `stdio` type only) How to handle `stderr` of the child process. Options: `"pipe"`, `"ignore"`, `"inherit"`, or a non-negative integer (file descriptor). Defaults to `"inherit"`.',
      'stderr: "inherit"',
    ],
    [
      'customUserVars',
      'Object',
      '(Optional) Defines custom variables that users can set for this MCP server, allowing for per-user credentials or configurations (e.g., API keys). These variables can then be referenced in `headers` or `env` fields.',
      'customUserVars:\n  API_KEY:\n    title: "API Key"\n    description: "Your personal API key."',
    ],
    [
      'oauth',
      'Object',
      '(Optional) OAuth2 configuration for authenticating with the MCP server. When configured, users will be prompted to authenticate via OAuth flow.',
      'oauth:\n  authorization_url: "https://example.com/oauth/authorize"\n  token_url: "https://example.com/oauth/token"',
    ],
    [
      'oauth_headers',
      'Object',
      '(Optional) Map of header names and values used only for OAuth flow requests, such as dynamic client registration or token exchange.',
      'oauth_headers:\n  Authorization: "Bearer ${DCR_API_KEY}"\n  X-Custom-Header: "custom_value"',
    ],
    [
      'obo',
      'Object',
      "(Optional, for `sse` and `streamable-http` types) On-Behalf-Of token exchange configuration. Exchanges the current user's OpenID access token for a delegated downstream token and forwards it as a Bearer token.",
      'obo:\n  scopes: "api://mcp-server-id/Mcp.Tools.ReadWrite"',
    ],
    [
      'startup',
      'Boolean',
      '(Optional) When set to false, this MCP server will not be connected at application startup.',
      'startup: false',
    ],
  ]}
/>

#### `title`

- **Type:** String (Optional)
- **Description:** Custom display name for the MCP server in the UI. If not specified, the server key name is used.
- **Validation:** Must start with a Unicode letter or number. The remaining characters may include Unicode letters, numbers, combining marks, spaces, hyphens, and straight or curly apostrophes.
- **Example:**
  ```yaml
  my-server:
    title: 'File System Access'
    command: npx
    args: ['-y', '@modelcontextprotocol/server-filesystem']
  ```

#### `description`

- **Type:** String (Optional)
- **Description:** Description of the MCP server, displayed in the UI to help users understand its purpose and capabilities.
- **Example:**
  ```yaml
  my-server:
    title: 'File System Access'
    description: 'Provides read/write access to local files and directories'
    command: npx
    args: ['-y', '@modelcontextprotocol/server-filesystem']
  ```

#### `type`

- **Type:** String
- **Description:** Specifies the connection type to the MCP server. Valid options are `"stdio"`, `"websocket"`, `"streamable-http"`, or `"sse"`.
- **Default Value:** Determined based on the presence and format of `url` or `command`.

#### `command`

- **Type:** String
- **Description:** (For `stdio` type) The command or executable to run to start the MCP server.

#### `args`

- **Type:** Array of Strings
- **Description:** (For `stdio` type) Command line arguments to pass to the `command`.

#### `url`

- **Type:** String
- **Description:** (For `websocket`, `streamable-http`, or `sse` type) The URL to connect to the MCP server. Supports dynamic user field placeholders (`{{LIBRECHAT_USER_*}}`) and environment variable substitution (`${ENV_VAR}`).
- **Notes:**
  - For `sse` type, the URL must start with `http://` or `https://`.
  - For `streamable-http` type, the URL must start with `http://` or `https://`.
  - For `websocket` type, the URL must start with `ws://` or `wss://`.

#### `proxy`

- **Type:** String (Optional, for `sse` and `streamable-http` types)
- **Description:** Outbound proxy URL for this remote MCP server. The value can reference environment variables with `${ENV_VAR}`.
- **Supported protocols:** `http://`, `https://`, `socks://`, and `socks5://`
- **Security note:** `proxy` is admin-controlled. It resolves environment variables, but does not resolve user-controlled placeholders such as `{{LIBRECHAT_USER_ID}}` or `customUserVars`.
- **Example:**
  ```yaml
  mcpServers:
    remote-api:
      type: streamable-http
      url: https://api.example.com/mcp
      proxy: '${MCP_PROXY_URL}'
  ```

#### `headers`

- **Type:** Object (Optional, for `sse` and `streamable-http` types)
- **Description:** Custom headers to send with the request. Supports various placeholder types for dynamic value substitution.
- **Placeholder Support:**
  - `{{LIBRECHAT_USER_ID}}`: Will be replaced with the current user's ID, enabling multi-user support.
  - `{{LIBRECHAT_USER_*}}`: Dynamic user field placeholders. Replace `*` with the UPPERCASE version of any allowed field.
  - `{{LIBRECHAT_OPENID_*}}`: OpenID token/session placeholders for YAML-defined servers.
  - `{{LIBRECHAT_GRAPH_*}}`: Microsoft Graph token placeholders for YAML-defined servers.
  - `{{LIBRECHAT_BODY_*}}`: Request body placeholders for YAML-defined servers, such as the current `conversationId`, `parentMessageId`, or `messageId`.
  - `{{CUSTOM_VARIABLE_NAME}}`: Replaced with the value provided by the user for a variable defined in `customUserVars` (e.g., `{{MY_API_KEY}}`).
  - `${ENV_VAR}`: Will be replaced with the value of the environment variable `{{ENV_VAR}}`.

**Available User Field Placeholders:**

| Placeholder                           | User Field         | Type             | Description                                                 |
| ------------------------------------- | ------------------ | ---------------- | ----------------------------------------------------------- |
| `{{LIBRECHAT_USER_NAME}}`             | `name`             | String           | User's display name                                         |
| `{{LIBRECHAT_USER_USERNAME}}`         | `username`         | String           | User's username                                             |
| `{{LIBRECHAT_USER_EMAIL}}`            | `email`            | String           | User's email address                                        |
| `{{LIBRECHAT_USER_PROVIDER}}`         | `provider`         | String           | Authentication provider (e.g., "email", "google", "github") |
| `{{LIBRECHAT_USER_ROLE}}`             | `role`             | String           | User's role (e.g., "user", "admin")                         |
| `{{LIBRECHAT_USER_GOOGLEID}}`         | `googleId`         | String           | Google account ID                                           |
| `{{LIBRECHAT_USER_FACEBOOKID}}`       | `facebookId`       | String           | Facebook account ID                                         |
| `{{LIBRECHAT_USER_OPENIDID}}`         | `openidId`         | String           | OpenID account ID                                           |
| `{{LIBRECHAT_USER_SAMLID}}`           | `samlId`           | String           | SAML account ID                                             |
| `{{LIBRECHAT_USER_LDAPID}}`           | `ldapId`           | String           | LDAP account ID                                             |
| `{{LIBRECHAT_USER_GITHUBID}}`         | `githubId`         | String           | GitHub account ID                                           |
| `{{LIBRECHAT_USER_DISCORDID}}`        | `discordId`        | String           | Discord account ID                                          |
| `{{LIBRECHAT_USER_APPLEID}}`          | `appleId`          | String           | Apple account ID                                            |
| `{{LIBRECHAT_USER_EMAILVERIFIED}}`    | `emailVerified`    | Boolean → String | Email verification status ("true" or "false")               |
| `{{LIBRECHAT_USER_TWOFACTORENABLED}}` | `twoFactorEnabled` | Boolean → String | 2FA status ("true" or "false")                              |
| `{{LIBRECHAT_USER_TERMSACCEPTED}}`    | `termsAccepted`    | Boolean → String | Terms acceptance status ("true" or "false")                 |
| `{{LIBRECHAT_USER_TERMSACCEPTEDAT}}`  | `termsAcceptedAt`  | Date → String    | Terms acceptance timestamp                                  |

**Note:** Missing fields will be replaced with empty strings.

`{{LIBRECHAT_BODY_*}}` placeholders are request-scoped. LibreChat creates the MCP connection for the active run, reuses it across tool calls in that run, and cleans it up when the request ends. Request-scoped servers are excluded from the persistent tool cache so request-specific headers and URLs are not reused outside the active run. Agent Builder attaches the complete server through one runtime-tools selection and resolves its tools after a run supplies body values. Completing OAuth stores authorization but defers the connection until that request context exists. `{{LIBRECHAT_USER_*}}`, `{{LIBRECHAT_OPENID_*}}`, and `{{LIBRECHAT_GRAPH_*}}` placeholders still make the server user-scoped, but HTTP transports refresh resolved headers before each tool call without forcing a reconnect by themselves.

`{{LIBRECHAT_BODY_PARENTMESSAGEID}}` receives the root-turn parent message in native Agent chats, OpenAI-compatible Agents API requests, and legacy Assistants runs. Open Responses requests have no compatible parent-message model, so a server that requires this placeholder fails closed on that interface; `previous_response_id` is not substituted for it.

OpenID access and ID tokens expire independently. `{{LIBRECHAT_OPENID_ACCESS_TOKEN}}` and the legacy `{{LIBRECHAT_OPENID_TOKEN}}` use the stored access-token expiry, while `{{LIBRECHAT_OPENID_ID_TOKEN}}` requires a current ID token. If a credential token is unavailable or expired, LibreChat returns an actionable `401` re-authentication error instead of sending an empty or literal bearer value; sign in again before retrying. For a request with no OpenID identity, a header containing one of those unresolved credential placeholders is omitted entirely. Identity metadata placeholders keep their existing empty-value behavior.

- **Example:**
  ```yaml
  headers:
    X-User-ID: '{{LIBRECHAT_USER_ID}}'
    X-User-Email: '{{LIBRECHAT_USER_EMAIL}}'
    X-User-Role: '{{LIBRECHAT_USER_ROLE}}'
    X-API-Key: '${SOME_API_KEY}'
    Authorization: 'Bearer ${SOME_AUTH_TOKEN}'
  ```

#### `apiKey`

- **Type:** Object (Optional, for `sse` and `streamable-http` types)
- **Description:** API key authentication configuration for the MCP server. Provides a structured way to configure API key-based authentication.
- **Sub-keys:**
  - `source`: String - Where the API key comes from. Options:
    - `"admin"`: API key is configured by the administrator (in environment variables or config)
    - `"user"`: API key is provided by the user through the UI
  - `authorization_type`: String - How the API key is sent in requests. Options:
    - `"bearer"`: Sent as `Authorization: Bearer <key>`
    - `"basic"`: Sent as `Authorization: Basic <key>`
    - `"custom"`: Sent in a custom header (requires `custom_header`)
  - `custom_header`: String - (Required when `authorization_type` is `"custom"`) The header name to use for the API key
- **Example:**

  ```yaml
  # Admin-provided API key with Bearer auth
  my-server:
    type: streamable-http
    url: https://api.example.com/mcp
    apiKey:
      source: 'admin'
      authorization_type: 'bearer'

  # User-provided API key with custom header
  another-server:
    type: sse
    url: https://api.example.com/sse
    apiKey:
      source: 'user'
      authorization_type: 'custom'
      custom_header: 'X-API-Key'
  ```

#### `iconPath`

- **Type:** String (Optional)
- **Description:** Defines the tool's display icon shown in the tool selection dialog.

#### `chatMenu`

- **Type:** Boolean (Optional)
- **Description:** When set to `false`, excludes the MCP server from the regular chat picker and picker-supplied chat requests. It remains available to saved Agents and model specs that explicitly assign it.
- **Default Value:** `true` (The MCP server can be selected in regular chat)

#### `serverInstructions`

- **Type:** Boolean or String (Optional)
- **Description:** Controls how MCP server instructions are injected into agent context. Server instructions provide high-level usage guidance for the entire MCP server, complementing individual tool descriptions.
- **Options:**
  - **`undefined`** (default): No instructions are included
  - **`true`**: Use server-provided instructions (if available) - ideal for well-documented servers with comprehensive guidance
  - **`false`**: Explicitly disable instructions - useful for saving context tokens or when tools are self-explanatory
  - **`string`**: Use custom instructions (overrides server-provided) - best for application-specific workflows or when server instructions are insufficient
- **Default Value:** `undefined` (no instructions included)
- **Notes:**
  - Instructions are automatically injected when `serverInstructions` is configured and the server's tools are available to the agent
  - Multiple servers can each contribute instructions to the agent context
  - LibreChat stores server-advertised text separately from the configured declaration. Inspection does not replace `true` or an operator-authored string, and Redis-backed registry entries from earlier builds are refreshed automatically during initialization.
- **Example:**

  ```yaml
  # Use server-provided instructions
  serverInstructions: true

  # Use custom instructions
  serverInstructions: |
    When using this filesystem server:
    1. Always use absolute paths for file operations
    2. Check file permissions before attempting write operations

  # Explicitly disable instructions
  serverInstructions: false
  ```

#### `env`

- **Type:** Object (Optional, `stdio` type only)
- **Description:** Environment variables to use when spawning the process.
- **Placeholder Support:**
  - `{{LIBRECHAT_USER_ID}}`: Replaced with the current user's ID.
  - `{{LIBRECHAT_USER_*}}`: Dynamic user field placeholders (e.g., `{{LIBRECHAT_USER_EMAIL}}`).
  - `{{CUSTOM_VARIABLE_NAME}}`: Replaced with the value provided by the user for a variable defined in `customUserVars` (e.g., `{{MY_API_KEY}}`).
  - `${ENV_VAR}`: Replaced with the value of the server-side environment variable `{{ENV_VAR}}`.

#### `timeout`

- **Type:** Integer (Optional)
- **Description:** Timeout in milliseconds for MCP server requests. Must be a non-negative integer.
- **Default Value:** `30000` (30 seconds)

#### `initTimeout`

- **Type:** Integer (Optional)
- **Description:** Timeout in milliseconds for MCP server initialization. Must be a non-negative integer.
- **Default Value:** `10000` (10 seconds)

#### `requiresOAuth`

- **Type:** Boolean (Optional, remote transports only: `sse`, `streamable-http`, `websocket`)
- **Description:** Whether this server requires OAuth authentication. If not specified, will be auto-detected during server startup. Although optional, it's best to set this value explicitly if you know whether the server requires OAuth or not.
- **Default Value:** Auto-detected if not specified
- **Notes:**
  - Applicable to remote (URL-based) transports: `sse`, `streamable-http`, and `websocket`. It has no effect on `stdio` servers, which have no URL to authenticate against.
  - Auto-detection occurs during server startup, which may add initialization time
  - Explicit configuration improves startup performance by skipping detection
  - Set `requiresOAuth: false` for servers protected only by a static `Authorization` header (e.g. a Bearer API key). Auto-detection probes the server _without_ your configured headers, so a server that answers `401` with a `WWW-Authenticate: Bearer` challenge can be misclassified as OAuth-protected; this flag bypasses that probe and lets your static header authenticate the connection normally.
  - Works with MCP OAuth environment variables (`MCP_OAUTH_ON_AUTH_ERROR`, `MCP_OAUTH_DETECTION_TIMEOUT`, `MCP_OAUTH_HANDLING_TIMEOUT`, `MCP_OAUTH_FLOW_TTL`) for enhanced connection management

#### `stderr`

- **Type:** String or Integer (Optional, `stdio` type only)
- **Description:** How to handle `stderr` of the child process. This matches the semantics of Node's [`child_process.spawn`](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options). Valid string values: `"pipe"`, `"ignore"`, `"inherit"`. Alternatively, a non-negative integer can be used as a file descriptor.
- **Default Value:** `"inherit"` (messages to `stderr` will be printed to the parent process's `stderr`).

#### `customUserVars`

- **Type:** Object (Optional)
- **Description:** Defines custom variables that users can set for this MCP server. This allows administrators to specify variables (e.g., API keys, URLs) that each user must configure individually. These user-provided values can then be used in `headers` or `env` configuration. Servers with `customUserVars` are automatically excluded from app-level connections, ensuring per-user credentials are always resolved at runtime.
- **Structure:**
  - The `customUserVars` object contains keys, where each key represents a variable name (e.g., `MY_API_KEY`). This name will be used in placeholders like `{{MY_API_KEY}}`.
  - Each variable name is an object with the following subkeys:
    - `title`: String (Required) - A user-friendly title for the variable, displayed in the configuration UI.
    - `description`: String (Optional) - A description or instructions for the variable, also displayed in the UI to guide the user. HTML can be used in this field (e.g., to create a link: `<a href="https://example.com" target="_blank">More info</a>`).
    - `sensitive`: Boolean (Optional) - Controls whether the value is treated as a secret and masked in the UI. Defaults to masked/secret behavior when omitted; set to `false` for non-secret fields such as project IDs or base URLs.
- **Usage in `headers` and `env`:**
  - Once defined under `customUserVars`, these variables can be referenced in the `headers` (for `sse` and `streamable-http` types) or `env` (for `stdio` type) sections using the `{{VARIABLE_NAME}}` syntax.
  - Users provide these values through the UI. These settings can be accessed in two ways:
    - **From Assistant Chat Input**: When selecting MCP tools for an assistant, a settings icon will appear next to configurable MCP servers in the tool selection dropdown. Clicking this icon opens a dialog to manage credentials for that server.
      <img
        src="/images/mcp/mcp_per_user_vars_assistant.png"
        alt="MCP Per-User Variables Configuration - Assistant Access"
        width={600}
      />
      <img
        src="/images/mcp/mcp_per_user_vars_assistant_dialog.png"
        alt="MCP Per-User Variables Configuration - Assistant Access Dialog"
        width={800}
      />
    - **From Settings Panel**: A dedicated "MCP Settings" section in the right panel lists all MCP servers with definable custom variables. Users can click on a server to open the configuration dialog to set or update their credentials for that specific MCP server.
      <img
        src="/images/mcp/mcp_per_user_vars_side_panel.png"
        alt="MCP Per-User Variables Configuration - Settings Panel Access"
        width={300}
      />
  - These user-provided values are stored securely, associated with the individual user and the specific MCP server, and substituted at runtime.
- **Example:**
  ```yaml
  customUserVars:
    MY_SERVICE_API_KEY:
      title: 'My Service API Key'
      description: "Your personal API access key for My Service. Find it at <a href='https://myservice.example.com/settings/api' target='_blank'>My Service API Settings</a>."
      sensitive: true
    SOME_OTHER_VAR:
      title: 'Some Other Variable'
      description: 'The specific value for some other configuration (e.g., a specific path or identifier).'
      sensitive: false
  ```
  Usage in `headers`:
  ```yaml
  headers:
    X-Auth-Token: '{{MY_SERVICE_API_KEY}}'
    X-Some-Other-Config: '{{SOME_OTHER_VAR}}'
  ```
  Usage in `env` (for `stdio` type):
  ```yaml
  env:
    API_KEY: '{{MY_SERVICE_API_KEY}}'
  ```

#### `obo`

- **Type:** Object (Optional, for `sse` and `streamable-http` types only)
- **Description:** Configures OAuth 2.0 On-Behalf-Of token exchange for an MCP server. LibreChat exchanges the logged-in user's OpenID access token for a delegated downstream token with the configured scopes, then forwards that token to the MCP server as an `Authorization: Bearer ...` header.
- **Required Subkeys:**
  - `scopes`: String - Non-empty scopes requested for the downstream token exchange.
- **Validation:**
  - `obo` is only valid for `sse` and `streamable-http` MCP servers.
  - `obo` is rejected for `stdio` and `websocket` servers.
  - Users need the `MCP_SERVERS.CONFIGURE_OBO` role permission to configure this field. Seed it with [`interface.mcpServers.configureObo`](/docs/configuration/librechat_yaml/object_structure/interface#mcpservers) or manage it from the admin panel.
- **Prerequisites:**
  - OpenID authentication must be configured with reusable access tokens.
  - Your identity provider and downstream application must allow the requested delegated scope.
- **Example:**
  ```yaml
  mcpServers:
    enterprise-tools:
      type: streamable-http
      url: https://api.example.com/mcp/
      obo:
        scopes: 'api://mcp-server-id/Mcp.Tools.ReadWrite'
  ```

See [OpenID Connect Token Reuse](/docs/configuration/authentication/OAuth2-OIDC/token-reuse) and [SharePoint Integration](/docs/configuration/sharepoint) for related token-reuse and delegated-token setup.

#### `oauth`

- **Type:** Object (Optional)
- **Description:** OAuth2 configuration for authenticating with the MCP server. When configured, users will be prompted to authenticate via OAuth flow before the MCP server can be used. If no client id & client secret is provided, Dynamic Client Registration (DCR) will be used.
- **Required Subkeys:**
  - `authorization_url`: String - The OAuth authorization endpoint URL
  - `token_url`: String - The OAuth token endpoint URL
  - `client_id`: String - OAuth client identifier
  - `client_secret`: String - OAuth client secret
  - `redirect_uri`: String - [OAuth redirect URI](/docs/features/mcp#oauth-callback-url) (eg. `http://localhost:3080/api/mcp/${serverName}/oauth/callback`)
  - `scope`: String - OAuth scopes (space-separated)
- **Optional Subkeys:**
  - `grant_types_supported`: Array of Strings - Supported grant types (defaults to `["authorization_code", "refresh_token"]`)
  - `token_endpoint_auth_methods_supported`: Array of Strings - Supported token endpoint authentication methods (defaults to `["client_secret_basic", "client_secret_post"]`)
  - `token_exchange_method`: String - Optional token exchange override. Use `default_post` to send client credentials in the form-encoded POST body, or `basic_auth_header` to send them in an HTTP Basic authorization header. When omitted, LibreChat discovers the provider's advertised token endpoint authentication methods and defaults to Basic auth if none are advertised.
  - `response_types_supported`: Array of Strings - Supported response types (defaults to `["code"]`)
  - `code_challenge_methods_supported`: Array of Strings - Supported PKCE code challenge methods (defaults to `["S256", "plain"]`)
  - `skip_code_challenge_check`: Boolean - Skip checking whether the OAuth provider advertises PKCE support. Useful for providers like AWS Cognito that support S256 but don't advertise it in their metadata. (defaults to `false`)
- **Environment variables:** YAML-defined OAuth URL fields, including `authorization_url`, `token_url`, `redirect_uri`, and `revocation_endpoint`, can use `${ENV_VAR}` references. LibreChat resolves the environment value before URL validation. User-managed OAuth endpoint URLs submitted through the UI must be literal URLs and reject `${ENV_VAR}` placeholders.
- **Example:**
  ```yaml
  oauth-api-server:
    authorization_url: https://api.example.com/oauth/authorize
    token_url: https://api.example.com/oauth/token
    client_id: your_client_id
    client_secret: your_client_secret
    redirect_uri: http://localhost:3080/api/mcp/oauth-api-server/oauth/callback
    scope: 'read execute'
    grant_types_supported: ['authorization_code', 'refresh_token']
    token_endpoint_auth_methods_supported: ['client_secret_post']
    token_exchange_method: default_post
    response_types_supported: ['code']
    code_challenge_methods_supported: ['S256', 'plain']
  ```

When `token_exchange_method` and `token_endpoint_auth_methods_supported` are both omitted for a preconfigured client secret, LibreChat allows up to five seconds for protected-resource and authorization-server metadata discovery. It uses advertised capabilities only when the discovered token endpoint exactly matches the configured `token_url`; discovery cannot redirect the configured client credentials to another endpoint. Set `token_exchange_method` explicitly when a provider does not publish usable metadata or needs a forced method.

#### `oauth_headers`

- **Type:** Object (Optional)
- **Description:** Headers used specifically for OAuth flow requests. These headers are used during OAuth authentication, such as dynamic client registration and token exchange, and are not sent during regular MCP server communication.
- **Common Use Cases:**
  - Adding authentication to dynamic client registration endpoints, such as `Authorization: Bearer ${DCR_API_KEY}`
  - Including custom provider-specific headers required for OAuth flows
  - Setting headers needed by OAuth token endpoints
- **Key Differences from `headers`:**
  - **`headers`**: Sent with regular MCP server requests after authentication is complete
  - **`oauth_headers`**: Sent only during OAuth authentication flows
- **Example:**
  ```yaml
  oauth_headers:
    Authorization: "Bearer ${DCR_API_KEY}"
    X-Custom-Header: "custom_value"
  ```

#### `startup`

- **Type:** Boolean (Optional)
- **Description:** When set to `false`, this MCP server will not be connected at application startup. This is useful for servers that require user input or configuration before connecting, or for cases where you want to control when the server is initialized.
- **Default Value:** `true`
- **Example:**
  ```yaml
  mcpServers:
    my-mcp-server:
      type: streamable-http
      url: 'https://api.example.com/mcp/'
      startup: false
  ```

### Notes

- **Type Inference:**
  - If `type` is omitted:
    - If `url` is specified and starts with `http://` or `https://`, `type` defaults to `sse`.
    - If `url` is specified and starts with `ws://` or `wss://`, `type` defaults to `websocket`.
    - If `command` is specified, `type` defaults to `stdio`.
- **Connection Types:**
  - **`stdio`**: Starts an MCP server as a child process and communicates via standard input/output.
  - **`websocket`**: Connects to an external MCP server via WebSocket.
  - **`sse`**: Connects to an external MCP server via Server-Sent Events (SSE).
  - **`streamable-http`**: Connects to an external MCP server via HTTP with support for streaming responses.
- **Internal/Local Addresses:**
  - **Important**: MCP servers using internal IP addresses (e.g., `172.24.1.165`, `192.168.1.100`), or local domains (e.g., `mcp-server`, `host.docker.internal`) **must** be explicitly allowed. Use [`mcpSettings.allowedAddresses`](/docs/configuration/librechat_yaml/object_structure/mcp_settings#allowedaddresses) for exact private host:port services when you want public destinations to remain reachable, or [`mcpSettings.allowedDomains`](/docs/configuration/librechat_yaml/object_structure/mcp_settings#alloweddomains) when you want a strict whitelist.
  - See [MCP Settings](/docs/configuration/librechat_yaml/object_structure/mcp_settings) for configuration details.

## Examples

### Configuration with Internal Addresses

When using internal/local MCP servers and no strict domain whitelist is needed, configure `mcpSettings.allowedAddresses` with the exact host and port:

```yaml filename="Complete MCP Configuration with Internal Servers"
# MCP Settings - Required for internal/local addresses
mcpSettings:
  allowedAddresses:
    - '172.24.1.165:8000' # Internal IP and port
    - 'mcp-prod:8001' # Docker container and port
    - 'host.docker.internal:8080' # Docker host and port

# MCP Servers - Individual configurations
mcpServers:
  prod-mcp:
    type: streamable-http
    url: http://172.24.1.165:8000/mcp
    timeout: 120000

  test-mcp:
    type: streamable-http
    url: http://mcp-prod:8001/mcp
    timeout: 120000
```

### `stdio` MCP Server

```yaml filename="stdio MCP Server"
puppeteer:
  type: stdio
  command: npx
  args:
    - -y
    - '@modelcontextprotocol/server-puppeteer'
  timeout: 30000
  initTimeout: 10000
  env:
    NODE_ENV: 'production'
    USER_EMAIL: '{{LIBRECHAT_USER_EMAIL}}'
    USER_ROLE: '{{LIBRECHAT_USER_ROLE}}'
  stderr: inherit
```

### `sse` MCP Server

```yaml filename="sse MCP Server"
everything:
  url: http://localhost:3001/sse
  headers:
    X-User-ID: '{{LIBRECHAT_USER_ID}}'
    X-API-Key: '${SOME_API_KEY}'
```

### `websocket` MCP Server

```yaml filename="websocket MCP Server"
myWebSocketServer:
  url: ws://localhost:8080
```

### `streamable-http` MCP Server

```yaml filename="streamable-http MCP Server"
streamable-http-server:
  type: streamable-http
  url: https://example.com/api/
  headers:
    X-User-ID: '{{LIBRECHAT_USER_ID}}'
    X-API-Key: '${SOME_API_KEY}'
```

### MCP Server with Dynamic User Fields

```yaml filename="MCP Server with User Fields"
user-aware-server:
  type: sse
  url: https://api.example.com/users/{{LIBRECHAT_USER_USERNAME}}/stream
  headers:
    X-User-ID: '{{LIBRECHAT_USER_ID}}'
    X-User-Email: '{{LIBRECHAT_USER_EMAIL}}'
    X-User-Role: '{{LIBRECHAT_USER_ROLE}}'
    X-Email-Verified: '{{LIBRECHAT_USER_EMAILVERIFIED}}'
    Authorization: 'Bearer ${API_TOKEN}'
```

### MCP Server with Per-User Credentials via `customUserVars`

```yaml filename="MCP Server with customUserVars"
my-mcp-server:
  type: streamable-http
  url: 'https://api.example-service.com/api/' # Example URL
  headers:
    X-Auth-Token: '{{API_KEY}}' # Uses the API_KEY defined below
  customUserVars:
    API_KEY: # This key will be used as {{API_KEY}} in headers/url
      title: 'API Key' # This is the label shown above the input field
      description: "Get your API key <a href='https://example.com/api-keys' target='_blank'>here</a>." # This description appears below the input
```

**NOTE** See [MCP Server Initialization](/docs/features/mcp#server-initialization) for more information about UI based server initialization.

### MCP Server with Custom Icon

```yaml filename="MCP Server with Icon"
filesystem:
  command: npx
  args:
    - -y
    - '@modelcontextprotocol/server-filesystem'
    - /home/user/LibreChat/
  iconPath: /home/user/LibreChat/client/public/assets/logo.svg
  chatMenu: false # Exclude from regular chat selection; Agent use remains available
```

### MCP Server with OAuth Authentication

```yaml filename="MCP Server with OAuth"
oauth-api-server:
  type: streamable-http
  url: https://api.example.com/mcp/
  oauth:
    authorization_url: https://api.example.com/oauth/authorize
    token_url: https://api.example.com/oauth/token
    client_id: your_client_id
    client_secret: your_client_secret
    redirect_uri: http://localhost:3080/api/mcp/oauth-api-server/oauth/callback
    scope: 'read execute'
  oauth_headers:
    X-Custom-Header: 'custom_value'
```

### MCP Server with Server Instructions

```yaml filename="MCP Server with Instructions"
# Server that uses its own provided instructions
web-search:
  type: streamable-http
  url: https://example.com/mcp/search
  serverInstructions: true

# Server with instructions explicitly disabled
filesystem:
  command: npx
  args:
    - -y
    - '@modelcontextprotocol/server-filesystem'
    - /home/user/documents/
  serverInstructions: false

# Server with custom instructions
puppeteer:
  type: stdio
  command: npx
  args:
    - -y
    - '@modelcontextprotocol/server-puppeteer'
  serverInstructions: |
    Browser automation security and best practices:
    1. Be cautious with local file access and internal IP addresses
    2. Take screenshots to verify successful page interactions
    3. Wait for page elements to load before interacting with them
    4. Use specific CSS selectors for reliable element targeting
    5. Check console logs for JavaScript errors when troubleshooting
```

### OAuth-Enabled MCP Server (Legacy requiresOAuth)

```yaml filename="OAuth-Enabled MCP Server"
composio-googlesheets:
  type: sse
  url: https://mcp.composio.dev/googlesheets/sse-endpoint
  requiresOAuth: true
  headers:
    X-User-ID: '{{LIBRECHAT_USER_ID}}'
    X-API-Key: '${COMPOSIO_API_KEY}'
  timeout: 45000
  initTimeout: 15000
```

**Related Environment Variables (Optional):**

```bash
# OAuth configuration for MCP servers
MCP_OAUTH_ON_AUTH_ERROR=true
MCP_OAUTH_DETECTION_TIMEOUT=10000
MCP_OAUTH_HANDLING_TIMEOUT=600000
MCP_OAUTH_FLOW_TTL=900000

# API key for the service
COMPOSIO_API_KEY=your_composio_api_key_here
```

---

**Importing MCP Server Configurations**

The `mcpServers` configurations allow LibreChat to dynamically interact with various MCP servers, which can perform specialized tasks or provide specific functionalities within the application. This modular approach facilitates extending the application's capabilities by simply adding or modifying server configurations.

---

## Additional Information

- **Default Behavior:**
  - Initialization happens at startup, and the app must be restarted for changes to take effect.
  - If both `url` and `command` are specified, the `type` must be explicitly defined to avoid ambiguity.
- **Multi-User Support:**
  - The MCPManager now supports distinct user-level and app-level connections, enabling proper connection management per user.
  - User connections are tracked and managed separately, with proper establishment and cleanup.
  - Use dynamic user field placeholders in headers, URLs, and environment variables:
    - `{{LIBRECHAT_USER_ID}}` - User's unique identifier
    - `{{LIBRECHAT_USER_EMAIL}}` - User's email address
    - `{{LIBRECHAT_USER_USERNAME}}` - User's username
    - `{{LIBRECHAT_USER_ROLE}}` - User's role (e.g., "user", "admin")
    - And many more fields (see headers section for complete list)
- **User Idle Management:**
  - User connections are monitored for activity and will be disconnected after 15 minutes of inactivity.
- **Environment Variables:**
  - **In `env` (for `stdio` type):** Useful for setting up specific runtime environments or configurations required by the MCP server process.
  - **In `headers` (for `sse` and `streamable-http` types):** Use `${ENV_VAR}` syntax to reference environment variables in header values.
- **Dynamic User Fields:**
  - User field placeholders are replaced at runtime with the authenticated user's information
  - Only non-sensitive fields are available (passwords and other sensitive data are excluded)
  - Missing fields default to empty strings
  - Boolean fields are converted to string representations ("true" or "false")
- **Error Handling (`stderr`):**
  - Configuring `stderr` allows you to manage how error messages from the MCP server process are handled. The default `"inherit"` means that the errors will be printed to the parent process's `stderr`.
- **Server Instructions:**
  - Instructions are automatically injected into the agent's system message when MCP server tools are used
  - Custom instructions (string values) take precedence over server-provided instructions
  - Multiple MCP servers can each contribute their own instructions to the agent context
  - Instructions are only included when the corresponding MCP server's tools are actually available to the agent
- **OAuth Authentication:**
  - OAuth2 flow is supported for secure authentication with MCP servers
  - Users will be prompted to authenticate via OAuth before the MCP server can be used

## References

- [Model Context Protocol (MCP) Documentation](https://github.com/modelcontextprotocol)
- [MCP Transports Specification](https://modelcontextprotocol.io/specification/draft/basic/transports)
- [Node.js child_process.spawn](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)

---

By properly configuring the `mcpServers` in your `librechat.yaml`, you can enhance LibreChat's functionality and integrate custom tools and services seamlessly.


# MCP Settings Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/mcp_settings)

## Overview

The `mcpSettings` configuration provides global settings for MCP (Model Context Protocol) server security and behavior. This configuration is separate from `mcpServers` and controls how MCP servers can connect to certain domains and IP addresses.

## Example

```yaml filename="MCP Settings Object Structure"
# Example MCP Settings Configuration
mcpSettings:
  # Strict whitelist mode:
  # allowedDomains:
  #   - "example.com"                    # Specific domain
  #   - "*.example.com"                  # All subdomains using wildcard
  #   - "https://api.example.com:8443"   # With protocol and port
  #   - "http://mcp-server:3000"         # Internal service, explicitly whitelisted

  # Default SSRF mode with private service exemptions:
  allowedAddresses:
    - "host.docker.internal:8080"        # Permit one private host on one port
    - "10.0.0.5:8000"                    # Permit one private IP on one port
```

## Configuration

### Subkeys

<OptionTable
  options={[
    ['allowedDomains', 'Array of Strings', 'A list specifying allowed domains for MCP server connections.', 'When configured, only listed domains are allowed. When not configured, SSRF targets are blocked but all other domains are allowed.'],
    ['allowedAddresses', 'Array of Strings', 'An SSRF exemption list, scoped to private IP space. Hostname/IP + port pairs listed here bypass the default-deny SSRF block when `allowedDomains` is not configured.', 'Use when you want default SSRF protection AND specific internal MCP servers, without flipping `allowedDomains` into strict-whitelist mode.'],
  ]}
/>

## allowedDomains

### Security Context (SSRF Protection)

LibreChat includes SSRF (Server-Side Request Forgery) protection with the following behavior:

**When `allowedDomains` is NOT configured:**
- SSRF-prone targets are **blocked by default**
- All other external domains are **allowed**

**When `allowedDomains` IS configured:**
- **Only** domains on the list are allowed
- Internal/SSRF targets can be allowed by explicitly adding them to the list

**Blocked SSRF targets include:**
- **Localhost** addresses (`localhost`, `127.0.0.1`, `::1`)
- **Private IP ranges** (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`)
- **Link-local addresses** (`169.254.0.0/16`, includes cloud metadata IPs)
- **Internal TLDs** (`.internal`, `.local`, `.localhost`)
- **Common internal service names** (`redis`, `mongodb`, `postgres`, `api`, `rag_api`, etc.)

If your MCP servers need to connect to internal services or Docker containers, either add them to the strict `allowedDomains` whitelist, or leave `allowedDomains` unset and add the exact private service to `allowedAddresses`.

### Pattern Formats

The `allowedDomains` array supports several pattern formats:

1. **Exact Domain Match**
   ```yaml
   allowedDomains:
     - "example.com"
   ```
   Only allows connections to exactly `example.com` (any protocol/port)

2. **Wildcard Subdomain Match**
   ```yaml
   allowedDomains:
     - "*.example.com"
   ```
   Allows connections to all subdomains of `example.com` (e.g., `api.example.com`, `mcp.example.com`)

3. **Specific IP Address**
   ```yaml
   allowedDomains:
     - "192.168.1.100"
     - "172.24.1.165"
   ```
   Allows connections to specific IP addresses

4. **Local Docker Domains**
   ```yaml
   allowedDomains:
     - "mcp-server"
     - "host.docker.internal"
   ```
   Allows connections to Docker container names or special Docker domains

5. **With Protocol and Port**
   ```yaml
   allowedDomains:
     - "https://api.example.com:8443"
     - "http://internal-mcp:3000"
   ```
   Restricts connections to specific protocol and port combinations

### Error Messages

If you see errors like:
```bash
  error: [MCPServersRegistry] Failed to inspect server "my-mcp": Domain "http://172.24.1.165:8000" is not allowed
  error: [MCP][my-mcp] Failed to initialize: Domain "http://172.24.1.165:8000" is not allowed
```

This likely indicates that the MCP server's private host and port need to be added to `allowedAddresses`, unless you intentionally use `allowedDomains` as a strict whitelist:

```yaml
mcpSettings:
  allowedAddresses:
    - "172.24.1.165:8000"    # Add the private host/IP and MCP port
```

## allowedAddresses

`allowedAddresses` is an **exemption list** for the SSRF private-IP block — not a domain whitelist. It is the right tool when you want to permit one or two specific private/internal services without restricting what your MCP servers can reach in the public internet.

### When to use it instead of `allowedDomains`

`allowedDomains` is a strict whitelist: when it is set, **only** listed entries are reachable. Adding a private IP there to permit, say, a self-hosted MCP server also blocks every public destination (`api.example.com`, `*.googleapis.com`, etc.) that you didn't also list.

`allowedAddresses` is used only when `allowedDomains` is not configured. It permits specific private `host:port` targets while leaving the rest of the public internet reachable through the default SSRF policy. Common configuration:

```yaml filename="default SSRF + permitted private host"
mcpSettings:
  allowedAddresses:
    - "host.docker.internal:8080"
    - "10.0.0.5:8000"
  # allowedDomains is intentionally not set — public destinations
  # remain reachable, only listed private host:port services are exempted.
```

If `allowedDomains` is configured, it is authoritative: private services must be listed there instead of relying on `allowedAddresses`.

### Acceptable entries

- **Hostnames with port**: `host.docker.internal:8080`, `mcp-server:3000`, `localhost:3001`
- **Private IPv4 literals with port**: `10.0.0.5:8000`, `127.0.0.1:3001`, `192.168.1.10:443`, `169.254.169.254:80`
- **Bracketed private IPv6 literals with port**: `[::1]:3001`, `[fc00::1]:8080`, `[fe80::1]:8080`

### Rejected entries (validated at config load)

- **URLs / paths / CIDR ranges**: `http://10.0.0.5`, `10.0.0.0/24`, `/path`
- **Bare hostnames or IPs**: `localhost`, `10.0.0.5`, `::1`, `[::1]` — every entry must include a port
- **Invalid ports**: `localhost:0`, `localhost:65536`, `localhost:http`
- **Public IP literals**: `8.8.8.8:53`, `1.1.1.1:53`, `[2001:4860::8888]:443` — the field is scoped to private IP space; public IPs are not SSRF targets and a public-IP exemption has no defensive purpose

### Hostname trust

A hostname entry trusts whatever IP that hostname resolves to at runtime on the listed port. If the DNS for a listed hostname is rotated or hijacked to point at a different private IP, the exemption follows. Only list hostnames whose DNS you control. **Prefer literal IPs when you can.**

## References

- [MCP Servers Configuration](/docs/configuration/librechat_yaml/object_structure/mcp_servers)
- [MCP Features](/docs/features/mcp)
- [Actions allowedAddresses](/docs/configuration/librechat_yaml/object_structure/actions#allowedaddresses) (similar concept for Actions)


# Skill Sync Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/skill_sync)

## Overview

The `skillSync` object lets admins mirror Skills from external sources. In v1.3.13, GitHub is the supported provider.

GitHub Skill Sync reads `SKILL.md` files and their bundled files from configured repository paths, stores them as Skills with `source: "github"`, and keeps mirrored rows aligned with the upstream repository on later syncs.

## Example

```yaml filename="librechat.yaml"
skillSync:
  github:
    enabled: true
    intervalMinutes: 60
    runOnStartup: true
    sources:
      - id: librechat-skills
        owner: your-org
        repo: your-skills-repo
        ref: main
        paths:
          - skills
        skillDiscoveryDepth: 2
        token: '${GITHUB_SKILLS_TOKEN}'
        # credentialKey: production-skills
        # tenantId: your-tenant-id
```

<Callout type="warning" title="Credentials">
  Use either `token` or `credentialKey` for each source, not both. `token` must be an environment
  variable reference such as `${GITHUB_SKILLS_TOKEN}`. Use a GitHub fine-grained personal access
  token scoped to the selected repository with read-only Contents and Metadata permissions.
</Callout>

## Top-Level Fields

### skillSync.github

<OptionTable
  options={[
    ['github.enabled', 'Boolean', 'Enables or disables GitHub Skill Sync.', 'enabled: true'],
    [
      'github.intervalMinutes',
      'Number',
      'How often scheduled GitHub Skill Sync runs. Must be between 5 and 35791 minutes.',
      'intervalMinutes: 60',
    ],
    [
      'github.runOnStartup',
      'Boolean',
      'Runs GitHub Skill Sync when the server starts.',
      'runOnStartup: true',
    ],
    [
      'github.sources',
      'Array of Objects',
      'GitHub repositories and paths to scan for Skills. Required when GitHub Skill Sync is enabled.',
      '',
    ],
  ]}
/>

**Defaults:**

- `enabled`: `false`
- `intervalMinutes`: `60`
- `runOnStartup`: `false`
- `sources`: `[]`

## Source Fields

Each `github.sources` entry configures one repository source.

<OptionTable
  options={[
    [
      'id',
      'String',
      'Stable unique source id. Must start with a letter or digit and contain only letters, digits, underscores, or hyphens.',
      'id: librechat-skills',
    ],
    ['owner', 'String', 'GitHub organization or user name.', 'owner: your-org'],
    ['repo', 'String', 'GitHub repository name.', 'repo: your-skills-repo'],
    ['ref', 'String', 'Git ref, branch, tag, or commit to read. Defaults to `main`.', 'ref: main'],
    [
      'paths',
      'Array of Strings',
      'Repository paths to scan. Use `.` to scan from the repository root.',
      'paths: ["skills"]',
    ],
    [
      'skillDiscoveryDepth',
      'Number',
      'Directory depth below each configured path to scan for `SKILL.md`. Defaults to `2`; maximum is `10`.',
      'skillDiscoveryDepth: 2',
    ],
    [
      'token',
      'String',
      'Environment variable reference containing a GitHub token. Mutually exclusive with `credentialKey`.',
      "token: '${GITHUB_SKILLS_TOKEN}'",
    ],
    [
      'credentialKey',
      'String',
      'Stored GitHub credential key managed through the admin Skill Sync credential endpoints. Mutually exclusive with `token`.',
      'credentialKey: production-skills',
    ],
    [
      'tenantId',
      'String',
      'Optional tenant id that owns mirrored Skills when tenant isolation is enabled.',
      'tenantId: tenant-a',
    ],
  ]}
/>

### Source Identity

The `id` field is part of the mirror identity. Keep it stable. Repointing a source to a renamed repository or a new ref keeps mirrored Skills linked to the same source id, while changing the source id creates a new mirror.

When `tenantId` is set, sync reads and writes run inside that tenant's isolation context. Treat `tenantId` as immutable for a source id: changing, adding, or removing it later leaves previously mirrored Skills in the old tenant.

## Sync Behavior

GitHub Skill Sync:

- Discovers `SKILL.md` files below each configured path.
- Imports the `SKILL.md` body, frontmatter, and bundled files.
- Stores source metadata such as source id, owner, repo, ref, skill path, commit SHA, and blob SHA.
- Updates mirrored Skills when the upstream file changes.
- Deletes mirrored Skills and files that were removed from the configured source.
- Validates and publishes Skills independently, so one invalid or conflicting Skill does not hide valid Skills from the same source.
- Preserves a skipped Skill's last-known-good mirror and rolls back incomplete writes for that Skill.
- Marks a run `partial` when it publishes at least one Skill but skips another, and records a bounded skipped count and details for administrator status reads.
- Uses a sync lock so only one GitHub sync run proceeds at a time.

Unknown `SKILL.md` frontmatter keys are accepted when their values remain within the bounded, JSON-safe extension limits. Invalid recognized fields, unsafe extension values, duplicate names, and ownership conflicts skip only the affected Skill. Authentication, rate-limit, lock-loss, repository access, and rollback failures remain source-level failures.

## Admin Endpoints

The admin API exposes GitHub Skill Sync status, manual runs, and stored credential management:

<OptionTable
  options={[
    [
      'GET /api/admin/skills/sync/status',
      'Endpoint',
      'Returns enabled state, schedule settings, source status, credential presence, recent sync counts, and bounded skipped-Skill details for partial runs.',
      '',
    ],
    [
      'POST /api/admin/skills/sync/run',
      'Endpoint',
      'Starts a manual sync run when the caller has the required admin Skills capability.',
      '',
    ],
    [
      'PUT /api/admin/skills/sync/credentials/:credentialKey',
      'Endpoint',
      'Stores or rotates a GitHub token for a credential key.',
      '',
    ],
    [
      'DELETE /api/admin/skills/sync/credentials/:credentialKey',
      'Endpoint',
      'Deletes a stored GitHub token for a credential key.',
      '',
    ],
  ]}
/>

Status reads require admin access and Skills read permission. Manual runs and credential writes require platform-level Skills management permission.


# Memory Configuration (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/memory)

## Overview

The `memory` object allows you to configure conversation memory and personalization features for the application. This configuration controls how the system remembers and personalizes conversations, including token limits, message context windows, and agent-based memory processing.

## Example

```yaml filename="memory"
memory:
  disabled: false
  validKeys: ['user_preferences', 'conversation_context', 'personal_info']
  tokenLimit: 2000
  charLimit: 10000
  maxInputTokens: 12000
  personalize: true
  messageWindowSize: 5
  agent:
    enabled: true
    provider: 'openAI'
    model: 'gpt-4'
    instructions: 'You are a helpful assistant that remembers user preferences and context.'
    model_parameters:
      temperature: 0.7
      max_tokens: 1000
```

## disabled

<OptionTable
  options={[
    [
      'disabled',
      'Boolean',
      'Disables memory functionality when set to true. When disabled, the system will not store or use conversation memory.',
      'disabled: false',
    ],
  ]}
/>

**Default:** `false`

```yaml filename="memory / disabled"
memory:
  disabled: true
```

## validKeys

<OptionTable
  options={[
    [
      'validKeys',
      'Array of Strings',
      'Specifies which keys are valid for memory storage. This helps control what types of information can be stored in memory.',
      'validKeys: ["user_name", "preferences", "context"]',
    ],
  ]}
/>

**Default:** No restriction (all keys are valid)

```yaml filename="memory / validKeys"
memory:
  validKeys:
    - 'user_preferences'
    - 'conversation_context'
    - 'personal_information'
    - 'learned_facts'
```

## tokenLimit

<OptionTable
  options={[
    [
      'tokenLimit',
      'Number',
      'Sets the maximum number of tokens that can be used for memory storage and processing.',
      'tokenLimit: 2000',
    ],
  ]}
/>

**Default:** No limit

```yaml filename="memory / tokenLimit"
memory:
  tokenLimit: 2000
```

## charLimit

<OptionTable
  options={[
    [
      'charLimit',
      'Number',
      'Sets the maximum number of characters allowed for individual memory entries. This prevents oversized memory payloads that could impact performance or exceed API limits.',
      'charLimit: 10000',
    ],
  ]}
/>

**Default:** `10000`

```yaml filename="memory / charLimit"
memory:
  charLimit: 10000
```

## maxInputTokens

<OptionTable
  options={[
    [
      'maxInputTokens',
      'Number',
      'Sets the maximum number of recent-chat tokens sent to the automatic memory agent before memory extraction. Long inputs are truncated from the beginning so the latest context is preserved.',
      'maxInputTokens: 12000',
    ],
  ]}
/>

**Default:** `12000`

```yaml filename="memory / maxInputTokens"
memory:
  maxInputTokens: 12000
```

## personalize

<OptionTable
  options={[
    [
      'personalize',
      'Boolean',
      'When set to true, gives users the ability to opt in or out of using memory features. Users can toggle memory on/off in their chat interface. When false, memory features are completely disabled.',
      'personalize: true',
    ],
  ]}
/>

**Default:** `true`

```yaml filename="memory / personalize"
memory:
  personalize: false
```

## messageWindowSize

<OptionTable
  options={[
    [
      'messageWindowSize',
      'Number',
      'Specifies the number of recent messages to include in the memory context window.',
      'messageWindowSize: 5',
    ],
  ]}
/>

**Default:** `5`

```yaml filename="memory / messageWindowSize"
memory:
  messageWindowSize: 10
```

## agent

<OptionTable
  options={[
    [
      'agent',
      'Object | Union',
      'Configures the optional automatic memory agent. Can be either a reference to an existing agent by ID or a complete agent configuration.',
      'agent: { enabled: true, provider: "openAI", model: "gpt-4" }',
    ],
  ]}
/>

Automatic extraction is opt-in. Set `agent.enabled: true` in either configuration format below. Omitting `enabled`, or setting it to `false`, keeps manual memory and Agent memory tools available without running the automatic memory agent on chat requests.

### enabled

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Enables automatic memory extraction with the configured memory agent.',
      'enabled: true',
    ],
  ]}
/>

**Default:** `false`

The `agent` field supports two different configuration formats:

### Agent by ID

When you have a pre-configured agent, you can reference it by its ID:

```yaml filename="memory / agent (by ID)"
memory:
  agent:
    enabled: true
    id: 'memory-agent-001'
```

### Custom Agent Configuration

For more control, you can define a complete agent configuration:

```yaml filename="memory / agent (custom)"
memory:
  agent:
    enabled: true
    provider: 'openAI'
    model: 'gpt-4'
    instructions: 'You are a memory assistant that helps maintain conversation context and user preferences.'
    model_parameters:
      temperature: 0.3
      max_tokens: 1500
      top_p: 0.9
```

#### Agent Configuration Fields

When using custom agent configuration, the following fields are available:

**provider** (required)

<OptionTable
  options={[
    [
      'provider',
      'String',
      'Specifies the AI provider for the memory agent. Can be a built-in provider (e.g., "openAI", "anthropic", "google") or a custom endpoint name.',
      'provider: "openAI"',
    ],
  ]}
/>

**model** (required)

<OptionTable
  options={[
    ['model', 'String', 'Specifies the model to use for memory processing.', 'model: "gpt-4"'],
  ]}
/>

**instructions** (optional)

<OptionTable
  options={[
    [
      'instructions',
      'String',
      'Custom instructions that replace the default instructions for when to set and/or delete memory. Should mainly be used when using validKeys that require specific information handling.',
      'instructions: "Only store user preferences and facts when explicitly mentioned."',
    ],
  ]}
/>

**model_parameters** (optional)

<OptionTable
  options={[
    [
      'model_parameters',
      'Object',
      'Additional parameters to pass to the model for fine-tuning its behavior. Values must be strings, numbers, or booleans.',
      'model_parameters: { temperature: 0.7 }',
    ],
  ]}
/>

## Complete Configuration Example

Here's a comprehensive example showing all memory configuration options:

```yaml filename="librechat.yaml"
version: 1.3.15
cache: true

memory:
  disabled: false
  validKeys:
    - 'user_preferences'
    - 'conversation_context'
    - 'learned_facts'
    - 'personal_information'
  tokenLimit: 3000
  charLimit: 10000
  maxInputTokens: 12000
  personalize: true
  messageWindowSize: 8
  agent:
    enabled: true
    provider: 'openAI'
    model: 'gpt-4'
    instructions: |
      Store memory using only the specified validKeys. For user_preferences: save 
      explicitly stated preferences about communication style, topics of interest, 
      or workflow preferences. For conversation_context: save important facts or 
      ongoing projects mentioned. For learned_facts: save objective information 
      about the user. For personal_information: save only what the user explicitly 
      shares about themselves. Delete outdated or incorrect information promptly.
    model_parameters:
      temperature: 0.2
      max_tokens: 2000
      top_p: 0.8
      frequency_penalty: 0.1
```

## Using Custom Endpoints

The memory feature supports custom endpoints. When using a custom endpoint, the `provider` field should match the custom endpoint's `name` exactly. Custom headers with environment variables and user placeholders are properly resolved.

```yaml filename="librechat.yaml with custom endpoint for memory"

endpoints:
    custom:
        - name: 'Custom Memory Endpoint'
           apiKey: 'dummy'
           baseURL: 'https://api.gateway.ai/v1'
           headers:
             x-gateway-api-key: '${GATEWAY_API_KEY}'
             x-gateway-virtual-key: '${GATEWAY_OPENAI_VIRTUAL_KEY}'
             X-User-Identifier: '{{LIBRECHAT_USER_EMAIL}}'
             X-Application-Identifier: 'LibreChat - Test'
             api-key: '${TEST_CUSTOM_API_KEY}'
           models:
             default:
               - 'gpt-4o-mini'
               - 'gpt-4o'
             fetch: false

memory:
  disabled: false
  tokenLimit: 3000
  maxInputTokens: 12000
  personalize: true
  messageWindowSize: 10
  agent:
    enabled: true
    provider: 'Custom Memory Endpoint'
    model: 'gpt-4o-mini'
```

- See [Custom Endpoint Headers](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#headers) for all available placeholders

## Notes

- Memory functionality enhances conversation continuity and personalization
- When `personalize` is true, users get a toggle in their chat interface to control memory usage
- Token limits help control memory usage and processing costs
- `maxInputTokens` bounds the chat context sent to the automatic memory agent, while `tokenLimit` controls stored memory usage
- Valid keys provide granular control over what information can be stored
- Custom `instructions` replace default memory handling instructions and should be used with `validKeys`
- `agent.enabled: true` is required for automatic memory extraction
- Agent configuration allows customization of automatic memory processing behavior
- When disabled, all memory features are turned off regardless of other settings
- The message window size affects how much recent context is considered for memory updates


# Summarization Configuration (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/summarization)

## Overview

The `summarization` configuration provides centralized control over conversation summarization and context pruning. This replaces the per-endpoint `summarize` and `summaryModel` fields that were previously available on custom and Azure OpenAI endpoints.

When a conversation exceeds the model's context window, the summarization system automatically compresses older messages into a concise checkpoint summary. This allows conversations to continue indefinitely without losing important context. The system also includes **context pruning**, which progressively degrades large tool results in older messages to reclaim token space before summarization is needed.

After a summarization turn, the context usage gauge uses the persisted summary baseline plus post-summary turns instead of re-counting discarded pre-summary history. Cumulative usage and cost totals still include the full branch spend.

## Example

```yaml filename="summarization"
summarization:
  provider: 'openAI'
  model: 'gpt-4o-mini'
  maxSummaryTokens: 4096
  reserveRatio: 0.05
  trigger:
    type: 'token_ratio'
    value: 0.8
  retainRecent:
    turns: 2
    tokens: 2000
  contextPruning:
    enabled: true
    keepLastAssistants: 3
    softTrimRatio: 0.3
    hardClearRatio: 0.5
    minPrunableToolChars: 50000
    softTrim:
      maxChars: 4000
      headChars: 1500
      tailChars: 1500
    hardClear:
      enabled: true
      placeholder: '[Old tool result content cleared]'
```

## provider

<OptionTable
  options={[
    [
      'provider',
      'String',
      "The LLM provider to use for summarization calls. If omitted, uses the agent's own provider.",
      'provider: "openAI"',
    ],
  ]}
/>

**Default:** Agent's own provider

## model

<OptionTable
  options={[
    [
      'model',
      'String',
      "The model to use for summarization calls. If omitted, uses the agent's own model.",
      'model: "gpt-4o-mini"',
    ],
  ]}
/>

**Default:** Agent's own model

## parameters

<OptionTable
  options={[
    [
      'parameters',
      'Object',
      'Additional LLM parameters for summarization requests (e.g., temperature, top_p).',
      'parameters: { temperature: 0.3 }',
    ],
  ]}
/>

## prompt

<OptionTable
  options={[
    [
      'prompt',
      'String',
      'Custom prompt for initial summarization. Replaces the built-in checkpoint prompt.',
      '',
    ],
  ]}
/>

**Default:** A structured checkpoint prompt that produces sections for Goal, Constraints & Preferences, Progress, Key Decisions, Next Steps, and Critical Context.

## updatePrompt

<OptionTable
  options={[
    [
      'updatePrompt',
      'String',
      'Custom prompt for re-compaction when a prior summary already exists. Used when the summary needs to be updated with new conversation content.',
      '',
    ],
  ]}
/>

**Default:** A built-in prompt that merges new messages into the existing checkpoint, compresses older details, and gives recent actions more detail.

## maxSummaryTokens

<OptionTable
  options={[
    [
      'maxSummaryTokens',
      'Number',
      'Maximum number of output tokens for the summarization model response.',
      'maxSummaryTokens: 4096',
    ],
  ]}
/>

## reserveRatio

<OptionTable
  options={[
    [
      'reserveRatio',
      'Number',
      'Fraction of the token budget reserved as headroom (0–1). Prevents the context from being filled to absolute capacity.',
      'reserveRatio: 0.05',
    ],
  ]}
/>

**Default:** `0.05` (5% headroom)

## trigger

<OptionTable
  options={[
    [
      'trigger',
      'Object',
      'Defines when summarization is activated. If omitted, summarization fires whenever message pruning drops any messages.',
      '',
    ],
  ]}
/>

### trigger Sub-keys

<OptionTable
  options={[
    [
      'type',
      'String',
      'The trigger strategy. Options: `"token_ratio"`, `"remaining_tokens"`, `"messages_to_refine"`.',
      'type: "token_ratio"',
    ],
    [
      'value',
      'Number',
      'The threshold value for the chosen trigger type. For `token_ratio`: 0–1 (inclusive). For `remaining_tokens` and `messages_to_refine`: positive integer.',
      'value: 0.8',
    ],
  ]}
/>

### Trigger Types

| Type                 | Value            | Fires When                                                                    |
| -------------------- | ---------------- | ----------------------------------------------------------------------------- |
| `token_ratio`        | `0.0–1.0`        | The fraction of context tokens used reaches or exceeds the value              |
| `remaining_tokens`   | Positive integer | The remaining context tokens drops to or below the value                      |
| `messages_to_refine` | Positive integer | The count of messages eligible for summarization reaches or exceeds the value |
| _(not set)_          | —                | Whenever pruning drops any messages (default behavior)                        |

**Example:**

```yaml filename="summarization / trigger"
summarization:
  trigger:
    type: 'remaining_tokens'
    value: 8000
```

## retainRecent

Controls how much of the newest conversation content remains verbatim outside the generated summary. This helps preserve recent instructions, tool results, and conversational continuity during compaction.

<OptionTable
  options={[
    [
      'turns',
      'Number',
      'Number of recent conversation turns to retain. Accepts values from 0 through 20.',
      '',
    ],
    [
      'tokens',
      'Number',
      'Positive token budget used when selecting recent content to retain.',
      '',
    ],
  ]}
/>

```yaml filename="summarization / retainRecent"
summarization:
  retainRecent:
    turns: 2
    tokens: 2000
```

Both fields are optional. Omit `retainRecent` to use the Agents SDK's normal compaction behavior.

## contextPruning

<OptionTable
  options={[
    [
      'contextPruning',
      'Object',
      'Configures position-based tool result degradation. Large tool results in older messages are progressively trimmed or cleared to reclaim token space.',
      '',
    ],
  ]}
/>

Context pruning is an opt-in feature that operates independently of summarization. It targets large tool call results in older messages, applying two progressive stages:

1. **Soft trim** — Truncates tool results to keep only the head and tail portions, with an ellipsis in between
2. **Hard clear** — Replaces the entire tool result with a short placeholder

Both stages are position-based: messages closer to the beginning of the conversation (older) are pruned first.

When Agent context pruning is enabled, LibreChat retains the compact adaptive fading tier chosen for each Agent and seeds it into later runs. This keeps the provider-only projection stable across turns, human-in-the-loop resumes, stops, branches, and replica handoffs, which improves Anthropic prompt-cache reuse. Stored conversation messages and tool results remain complete; the faded projection is not written back over canonical history.

### contextPruning Sub-keys

<OptionTable
  options={[
    ['enabled', 'Boolean', 'Enables position-based tool result degradation.', 'enabled: true'],
    [
      'keepLastAssistants',
      'Number',
      'Number of recent assistant turns to protect from any pruning.',
      'keepLastAssistants: 3',
    ],
    [
      'softTrimRatio',
      'Number',
      'Age ratio (0–1) at which soft-trim activates. Messages older than this ratio of the conversation are candidates for soft-trimming.',
      'softTrimRatio: 0.3',
    ],
    [
      'hardClearRatio',
      'Number',
      'Age ratio (0–1) at which hard-clear activates. Messages older than this ratio are candidates for full replacement.',
      'hardClearRatio: 0.5',
    ],
    [
      'minPrunableToolChars',
      'Number',
      'Minimum character count of a tool result before pruning applies. Smaller results are left untouched.',
      'minPrunableToolChars: 50000',
    ],
    ['softTrim', 'Object', 'Configuration for the soft-trim stage.', ''],
    ['hardClear', 'Object', 'Configuration for the hard-clear stage.', ''],
  ]}
/>

**Defaults:**

| Field                  | Default |
| ---------------------- | ------- |
| `enabled`              | `false` |
| `keepLastAssistants`   | `3`     |
| `softTrimRatio`        | `0.3`   |
| `hardClearRatio`       | `0.5`   |
| `minPrunableToolChars` | `50000` |

### softTrim Sub-keys

<OptionTable
  options={[
    [
      'maxChars',
      'Number',
      'Maximum total characters after soft-trimming a tool result.',
      'maxChars: 4000',
    ],
    [
      'headChars',
      'Number',
      'Number of characters to preserve from the beginning of the tool result.',
      'headChars: 1500',
    ],
    [
      'tailChars',
      'Number',
      'Number of characters to preserve from the end of the tool result.',
      'tailChars: 1500',
    ],
  ]}
/>

**Defaults:** `maxChars: 4000`, `headChars: 1500`, `tailChars: 1500`

### hardClear Sub-keys

<OptionTable
  options={[
    [
      'enabled',
      'Boolean',
      'Whether the hard-clear stage is active. When disabled, only soft-trim is applied.',
      'enabled: true',
    ],
    [
      'placeholder',
      'String',
      'Placeholder text that replaces the full tool result content when hard-cleared.',
      'placeholder: "[Old tool result content cleared]"',
    ],
  ]}
/>

**Defaults:** `enabled: true`, `placeholder: "[Old tool result content cleared]"`

**Example:**

```yaml filename="summarization / contextPruning"
summarization:
  contextPruning:
    enabled: true
    keepLastAssistants: 5
    softTrimRatio: 0.25
    hardClearRatio: 0.6
    minPrunableToolChars: 30000
    softTrim:
      maxChars: 6000
      headChars: 2500
      tailChars: 2500
    hardClear:
      enabled: true
      placeholder: '[Content removed for context management]'
```

## Complete Configuration Example

```yaml filename="librechat.yaml"
version: 1.3.15
cache: true

summarization:
  provider: 'openAI'
  model: 'gpt-4o-mini'
  maxSummaryTokens: 4096
  reserveRatio: 0.05
  trigger:
    type: 'token_ratio'
    value: 0.8
  retainRecent:
    turns: 2
    tokens: 2000
  contextPruning:
    enabled: true
    keepLastAssistants: 3
    softTrimRatio: 0.3
    hardClearRatio: 0.5
    minPrunableToolChars: 50000
    softTrim:
      maxChars: 4000
      headChars: 1500
      tailChars: 1500
    hardClear:
      enabled: true
      placeholder: '[Old tool result content cleared]'
```

## Migration from Per-Endpoint Settings

If you previously used `summarize` and `summaryModel` on custom or Azure OpenAI endpoints:

```yaml filename="Before (removed)"
endpoints:
  custom:
    - name: 'My Endpoint'
      summarize: true
      summaryModel: 'gpt-3.5-turbo'
```

These fields have been removed. Use the top-level `summarization` configuration instead:

```yaml filename="After"
summarization:
  model: 'gpt-4o-mini'
```

## Notes

- Summarization is configured globally rather than per-endpoint
- The `summarize` and `summaryModel` fields on custom endpoints and Azure OpenAI endpoints are no longer supported
- When `provider` and `model` are omitted, the agent's own provider and model are used for summarization
- `retainRecent` keeps a recent verbatim tail outside the summary; `turns` accepts `0-20` and `tokens` must be positive
- Context pruning is disabled by default and must be explicitly enabled with `contextPruning.enabled: true`
- Context pruning only affects tool call results that exceed `minPrunableToolChars` — smaller results are never pruned
- The `keepLastAssistants` setting protects recent turns from pruning regardless of the trim/clear ratios
- Custom `prompt` and `updatePrompt` values fully replace the built-in prompts — use with care
- Set `AGENT_DEBUG_LOGGING=true` in your `.env` file to enable verbose logging of token counts and context pruning diagnostics


# Content Filter Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/message_filter)

## Overview

LibreChat provides two server-side filter configurations:

- `filters` applies source-aware policy to selected fields across messages, prompts, agents, files, skills, memories, tools, and other reusable content.
- `messageFilter` is the legacy message-text filter. Existing deployments can keep using it while migrating to `filters.messages`.

Both are opt-in. When both are configured, both policies apply.

<Callout type="warning" title="Base configuration only">
  `filters` is loaded only from the base `librechat.yaml`. Database, role, group, and user
  overrides cannot add, change, or remove it. In a multi-replica deployment, roll out or restart
  every replica together and verify that each loaded the same base configuration before treating
  the policy as active.
</Callout>

## Source-Aware Filters

Each source has an optional `pii` policy. Omit `filters`, a source, or its `pii` block to leave that scope disabled.

```yaml filename="librechat.yaml"
filters:
  messages:
    unattributedAssistantContent: model_output
    pii:
      action: audit
      fields: [text, summary, attachment_reference]
      starterPatterns: [sk_prefix, bearer_header, api_key_header]
      customPatterns:
        - id: organization_identifier
          label: Organization identifier
          regex: 'ORG-[A-Z0-9]{12}'
  files:
    pii:
      fields: [name, content, extracted_text, transcript]
      uninspectable: block
  skills:
    pii:
      fields: [name, description, instructions, imported_text, file_text]
```

### Pattern Configuration

For each enabled source:

- Omit `action` or set it to `block` to reject matches. Set `action: audit` for a shadow rollout that records findings without rejecting, changing, or redacting content.
- Omit `fields` to inspect every supported field for that source. An explicit list must contain between 1 and 256 supported field names.
- Omit `starterPatterns` to enable the full starter catalog. Set it to `[]` to disable starter patterns while retaining any `customPatterns`.
- Configure `customPatterns` with an `id`, user-facing `label`, and `regex`.

Audit mode evaluates all configured fragments and rules so operators can measure findings before enforcement. Each info-level audit record identifies the action, detector, rule, label, source, field, and provenance, but never includes the matched text or source content. Audit mode does not provide redaction; unsupported action values are rejected when the configuration loads.

Actions are configured independently per source, so one source can audit while another blocks. `filters.files.pii.uninspectable: block` remains an independent fail-closed rule for opaque or oversized selected file content, even when that source's pattern action is `audit`.

The built-in starter pattern IDs are:

- `sk_prefix`: `sk-`-style token prefixes.
- `bearer_header`: bearer tokens in text.
- `api_key_header`: `api-key` header-shaped text.

Custom patterns use RE2JS's bounded, linear-time regular-expression syntax. Backreferences and lookaround are unsupported, and some escapes differ from JavaScript regular expressions. LibreChat rejects invalid or unsupported patterns while loading the configuration.

### Supported Sources and Fields

| Source | Supported fields |
| --- | --- |
| `messages` | `name`, `text`, `summary`, `quote`, `answer`, `decision_response`, `decision_reason`, `content_part`, `attachment_reference`, `assembled_context` |
| `prompts` | `name`, `description`, `oneliner`, `category`, `command`, `text`, `preset_text`, `system`, `context`, `instructions`, `additional_instructions`, `greeting`, `example_input`, `example_output` |
| `agentInstructions` | `name`, `category`, `description`, `instructions`, `additional_instructions`, `edge_description`, `edge_prompt`, `edge_prompt_key`, `artifacts`, `support_contact_name`, `support_contact_email` |
| `conversationStarters` | `text` |
| `conversationTitles` | `title` |
| `feedback` | `text` |
| `skills` | `name`, `display_title`, `description`, `category`, `frontmatter`, `instructions`, `imported_text`, `file_name`, `file_text` |
| `memories` | `key`, `value`, `summary` |
| `files` | `name`, `content`, `extracted_text`, `transcript`, `uri` |
| `toolArguments` | `name`, `arguments`, `output` |
| `modelParameters` | `stop`, `request_fields`, `response_format`, `metadata` |
| `actionMetadata` | `raw_spec`, `domain`, `privacy_policy_url`, `authorization_type`, `custom_auth_header`, `authorization_content_type`, `authorization_url`, `client_url`, `scope`, `token_exchange_method`, `api_key`, `oauth_client_id`, `oauth_client_secret` |

### Message Provenance

`filters.messages.unattributedAssistantContent` controls how legacy assistant content without provenance is classified:

- `model_output` (default): preserves legacy behavior by treating unattributed assistant content as model output.
- `inspect`: treats otherwise unattributed assistant content, including selected attachment projections, as submitted content. Assistant content explicitly marked as model output remains exempt.

This classification can affect an enabled legacy `messageFilter.pii` policy even when `filters.messages.pii` is omitted. Inventory or migrate old records before relying on retroactive inspection.

### Uninspectable Files

`filters.files.pii.uninspectable` controls selected file content that LibreChat cannot inspect, including opaque or oversized content:

- `allow` (default): preserves compatibility and permits the content.
- `block`: rejects it before provider or storage side effects.

Roll out `block` deliberately. It can make older files unavailable for reuse until inspectable text is present.

### Stored and Reused Content

Enabling or changing a policy does not rewrite or delete stored records. LibreChat applies the current policy when protected content is submitted, copied, shared, reused, or becomes model-bound.

Safe metadata edits can still succeed so blocked records remain repairable. In management views, blocked prompt or preset fields can be blank with `contentFilterBlocked: true`; blocked prompt groups return an error on direct retrieval and are omitted from collection and reuse responses. A rejected background memory update can be skipped while the main chat response continues.

Blocking source-aware text matches return HTTP `400` with `content_filter_block`. Uninspectable file rejections use `content_filter_uninspectable`.

### Limits

- Up to 256 fields, starter patterns, or custom patterns per source.
- Up to 256 custom patterns across the full configuration.
- Pattern IDs up to 256 characters, labels up to 512 characters, and regexes up to 512 characters.
- Up to 8,192 total regex characters and 8,192 compiled filter instructions.

## Legacy `messageFilter`

`messageFilter.pii` rejects matching caller-supplied message text before moderation, model requests, or persistence with `message_filter_pii_block`. It remains block-only and does not accept `action`. It does not inspect file contents or the other reusable sources covered by `filters`.

```yaml filename="librechat.yaml"
messageFilter:
  pii:
    starterPatterns:
      - sk_prefix
      - bearer_header
      - api_key_header
    customPatterns:
      - id: anthropic_api_key
        label: Anthropic API key
        regex: 'sk-ant-[A-Za-z0-9_-]{20,}'
```

The legacy filter checks chat-route user text and caller-supplied text across all roles in OpenAI-compatible Chat Completions and Responses requests. It uses the same built-in IDs and linear-time custom-regex rules described above.


# Web Search Configuration (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/web_search)

The `webSearch` configuration allows you to customize the web search functionality within LibreChat, including search providers, content scrapers, and result rerankers.

## Overview

The web search feature consists of three main components:

1. **Search Providers**: Services that perform the initial web search
2. **Scrapers**: Services that extract content from web pages
3. **Rerankers**: Services that reorder search results for better relevance

## Example

```yaml filename="webSearch"
webSearch:
  # Search Provider Configuration
  serperApiKey: "${SERPER_API_KEY}"
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
  searxngApiKey: "${SEARXNG_API_KEY}"
  searchProvider: "serper" # Options: "serper", "searxng", "tavily", "keenable"

  # Optional: query options sent to a SearXNG instance
  searxngSearchOptions:
    engines: "google,bing,startpage" # Default: "google,bing,duckduckgo"
    language: "en" # Default: "all"

  # Tavily Configuration (search and/or scraper)
  tavilyApiKey: "${TAVILY_API_KEY}"
  # Optional: custom Tavily-compatible endpoints
  tavilySearchUrl: "${TAVILY_SEARCH_URL}"
  tavilyExtractUrl: "${TAVILY_EXTRACT_URL}"

  # Keenable Configuration (search and/or scraper; all credentials optional)
  keenableApiKey: "${KEENABLE_API_KEY}"
  keenableApiUrl: "${KEENABLE_API_URL}"
  keenableSearchOptions:
    maxResults: 8
    site: "example.com"
    attributionTitle: "LibreChat"
    timeout: 15000
  keenableScraperOptions:
    attributionTitle: "LibreChat"
    timeout: 15000

  # Scraper Configuration
  firecrawlApiKey: "${FIRECRAWL_API_KEY}"
  firecrawlApiUrl: "${FIRECRAWL_API_URL}"
  firecrawlVersion: "${FIRECRAWL_VERSION}"
  scraperProvider: "firecrawl" # Options: "firecrawl", "serper", "tavily", "keenable"

  # Reranker Configuration
  jinaApiKey: "${JINA_API_KEY}"
  jinaApiUrl: "${JINA_API_URL}"
  cohereApiKey: "${COHERE_API_KEY}"
  rerankerType: "jina" # Options: "jina", "cohere", "none"

  # General Settings
  scraperTimeout: 7500 # Timeout in milliseconds for scraper requests (default: 7500)
  safeSearch: 1 # Options: 0 (OFF), 1 (MODERATE - default), 2 (STRICT)
  # Required for deliberately private self-hosted provider endpoints
  allowedAddresses:
    - "searxng:8080"
```

## SSRF Protection and Private Providers

LibreChat validates each connection it opens for web search, scraping, and reranking. Private, loopback, link-local, and cloud-metadata destinations are blocked by default, including redirect hops and hostnames that resolve to a private address at connect time.

Use `webSearch.allowedAddresses` when a configured SearXNG, Firecrawl, Jina, Tavily-compatible, Keenable-compatible, or related provider endpoint is deliberately private:

```yaml filename="webSearch / allowedAddresses"
webSearch:
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
  allowedAddresses:
    - "searxng:8080"
    - "10.0.0.5:3002"
    - "[fd00::5]:8080"
```

This field is an SSRF exemption list, not a strict allowlist. Public destinations remain available. Entries must be exact `host:port`, `private.ip:port`, or `[ipv6]:port` pairs; URLs, paths, CIDR ranges, bare hosts/IPs, and public IP literals are rejected. An exempted hostname trusts whichever private IP it resolves to on that port, so prefer a private IP literal and list only names whose DNS you control.

LibreChat automatically exempts a configured HTTP(S) forward-proxy endpoint so it can be reached. When the proxy carries a request, it resolves the destination and must enforce destination egress itself. `NO_PROXY` can make a request direct, where LibreChat's connection guard still applies. The guard covers connections LibreChat opens to provider APIs; it cannot constrain a page URL that a third-party scraper receives as request data and fetches on its own infrastructure.

## Search Providers

### searchProvider

<OptionTable
  options={[
    ['searchProvider', 'String', 'Specifies which search provider to use.', 'Options: "serper", "searxng", "tavily", "keenable"'],
  ]}
/>

### serperApiKey

<OptionTable
  options={[
    ['serperApiKey', 'String', 'Environment variable name for the Serper API key. If not set in .env, users will be prompted to provide it via UI.', '${SERPER_API_KEY}'],
  ]}
/>

**Note:** Get your API key from [Serper.dev](https://serper.dev/api-keys)


### searxngInstanceUrl

<OptionTable
  options={[
    ['searxngInstanceUrl', 'String', 'Environment variable name for the SearXNG instance URL. If not set in .env, users will be prompted to provide it via UI.', '${SEARXNG_INSTANCE_URL}'],
  ]}
/>

### searxngApiKey

<OptionTable
  options={[
    ['searxngApiKey', 'String', 'Environment variable name for the SearXNG API key. If not set in .env, users will be prompted to provide it via UI.', '${SEARXNG_API_KEY}'],
  ]}
/>

**Note:** This is optional and only needed if your SearXNG instance requires authentication.

### searxngSearchOptions

<OptionTable
  options={[
    ['searxngSearchOptions', 'Object', 'Query options sent to your SearXNG instance on every search. Every subkey is optional, and the block is only read when searchProvider is "searxng".', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['engines', 'String or Array of Strings', 'Engines your instance should query, sent to SearXNG as the engines parameter. Accepts a comma-separated string or a YAML list; both are normalized to the comma-separated form, with surrounding whitespace and empty entries dropped. A value that is empty once trimmed is treated as unset.', 'Default: "google,bing,duckduckgo"'],
    ['language', 'String', 'Result language code, sent to SearXNG as the language parameter. Use a code your instance accepts, such as "en", "de", or "fr".', 'Default: "all"'],
    ['timeRange', 'String', 'Restricts results by publish date, sent to SearXNG as time_range. Left out of the request entirely when unset.', 'Options: "day", "month", "year"'],
    ['timeout', 'Number', 'HTTP request timeout in milliseconds for calls to your instance. Must be a positive integer no greater than 120000; 0 is rejected because it would disable the timeout.', 'Default: 10000'],
  ]}
/>

<Callout type="info" title="Availability">
`searxngSearchOptions` reaches SearXNG through the search tool in `@librechat/agents`, and needs `@librechat/agents` v3.6.9 or later. Releases built against an earlier version ignore the block rather than failing to start, so an instance that keeps returning default results may simply be running an older build.
</Callout>

**Example:**

```yaml filename="webSearch"
webSearch:
  searchProvider: "searxng"
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
  searxngApiKey: "${SEARXNG_API_KEY}" # Optional
  searxngSearchOptions:
    engines:
      - google
      - bing
      - startpage
      - qwant
    language: "en"
    timeRange: "month"
    timeout: 10000
```

`engines` also accepts the comma-separated form SearXNG itself uses, so this is equivalent to the list above:

```yaml filename="webSearch"
webSearch:
  searxngSearchOptions:
    engines: "google, bing, startpage, qwant"
```

#### Choosing engines

Engine names must match engines that are enabled on your own instance, and two things follow from that:

- SearXNG ignores an engine it does not recognize instead of reporting an error. A typo, or an engine that is disabled on your instance, shows up as fewer results rather than as a failure.
- The valid names are instance-specific. Check the enabled list at `https://your-instance/config`, which returns JSON including every enabled engine, or open the **Engines** tab of your instance preferences page.

The default is `google,bing,duckduckgo`. SearXNG aggregates whatever the selected engines return, so one blocked engine costs you its share of the results rather than the whole response, and a search only comes back empty when every selected engine fails or returns nothing.

DuckDuckGo is the engine in that default set most likely to be blocked, since it serves CAPTCHAs to most self-hosted instances. With only three engines in the set, losing it at the same time as a rate-limited Google or Bing is a common way to end up with no results at all. Setting `engines` explicitly, leaving DuckDuckGo out, and listing more than three engines all reduce that risk.

Engines that answer quickly and tolerate self-hosted traffic make the best starting set, for example:

```yaml filename="webSearch"
webSearch:
  searchProvider: "searxng"
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
  searxngSearchOptions:
    engines:
      - google
      - bing
      - brave
      - startpage
      - qwant
```

Adding more engines widens coverage but also slows every search down, since SearXNG waits on the slowest engine in the set before responding. Keep `timeout` in mind when you grow the list.

#### What these options do not control

- **Safe search** is not part of this block. It comes from the top-level [`safeSearch`](#safesearch) key and is forwarded to SearXNG as `safesearch`.
- **Result categories** are chosen per query by LibreChat, which maps the search type to `general`, `images`, `videos`, or `news`. They cannot be overridden here.
- **Page number and result format** are fixed. LibreChat always requests page 1 in JSON format, which is why your instance must have `json` enabled under `formats`.

### tavilyApiKey

<OptionTable
  options={[
    ['tavilyApiKey', 'String', 'Environment variable name for the Tavily API key. Used for both search and scraper. If not set in .env, users will be prompted to provide it via UI.', '${TAVILY_API_KEY}'],
  ]}
/>

**Note:** Get your API key from [Tavily](https://app.tavily.com/home)

### tavilySearchUrl

<OptionTable
  options={[
    ['tavilySearchUrl', 'String', 'Environment variable name for a custom Tavily Search API URL. Optional; defaults to Tavily hosted search when unset.', '${TAVILY_SEARCH_URL}'],
  ]}
/>

### tavilyExtractUrl

<OptionTable
  options={[
    ['tavilyExtractUrl', 'String', 'Environment variable name for a custom Tavily Extract API URL. Optional; defaults to Tavily hosted extract when unset.', '${TAVILY_EXTRACT_URL}'],
  ]}
/>

### tavilySearchOptions

<OptionTable
  options={[
    ['tavilySearchOptions', 'Object', 'Configuration options for Tavily search.', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['searchDepth', 'String', 'Controls relevance vs latency tradeoff. "basic" returns one NLP summary per URL. "advanced" returns multiple semantically relevant snippets per URL (2 API credits). "fast" balances speed and relevance with snippets. "ultra-fast" minimizes latency with one NLP summary.', 'Options: "basic", "advanced", "fast", "ultra-fast". Default: "basic"'],
    ['maxResults', 'Number', 'The maximum number of search results to return.', 'Range: 1-20. Default: 5'],
    ['topic', 'String', 'The category of the search. "news" is useful for real-time updates. "finance" for financial data.', 'Options: "general", "news", "finance". Default: "general"'],
    ['includeImages', 'Boolean', 'Include images in the response. Returns both top-level query images and per-result images.', 'Default: false'],
    ['includeAnswer', 'Boolean or String', 'Include an LLM-generated answer. "basic" or true for a quick answer, "advanced" for a detailed answer.', 'Default: false'],
    ['includeRawContent', 'Boolean or String', 'Include cleaned and parsed HTML content. "markdown" or true for markdown format, "text" for plain text.', 'Default: false'],
    ['includeDomains', 'Array of Strings', 'Restrict search to specific domains. Maximum 300 domains.', ''],
    ['excludeDomains', 'Array of Strings', 'Exclude specific domains from results. Maximum 150 domains.', ''],
    ['timeRange', 'String', 'Time range filter based on publish or last updated date.', 'Options: "day", "week", "month", "year"'],
    ['includeImageDescriptions', 'Boolean', 'When includeImages is true, also add a descriptive text for each image.', 'Default: false'],
    ['includeFavicon', 'Boolean', 'Include the favicon URL for each search result.', 'Default: false'],
    ['chunksPerSource', 'Number', 'Maximum number of relevant content chunks per source. Only available when searchDepth is "advanced".', 'Range: 1-3. Default: 3'],
    ['safeSearch', 'Boolean', 'Optional Tavily safe_search override for Tavily Search requests. Omitted by default; true may require Tavily Enterprise.', 'Default: omitted'],
    ['timeout', 'Number', 'Client-side HTTP request timeout in milliseconds. Controls how long to wait for the Tavily API to respond before giving up.', 'Default: 15000'],
  ]}
/>

### keenableApiKey

<OptionTable
  options={[
    ['keenableApiKey', 'String', 'Environment variable name for the optional Keenable API key. Keenable works without a key; configuring one raises public rate limits.', '${KEENABLE_API_KEY}'],
  ]}
/>

### keenableApiUrl

<OptionTable
  options={[
    ['keenableApiUrl', 'String', 'Environment variable name for a custom Keenable search API URL. This does not change the page-fetch endpoint.', '${KEENABLE_API_URL}'],
  ]}
/>

The page-fetch endpoint is configured separately with the environment-only `KEENABLE_FETCH_URL` variable.

### keenableSearchOptions

<OptionTable
  options={[
    ['keenableSearchOptions', 'Object', 'Configuration options for Keenable search.', ''],
  ]}
/>

<OptionTable
  options={[
    ['maxResults', 'Number', 'Maximum number of search results.', 'Range: 1-20. Default: 8'],
    ['site', 'String', 'Restricts results to one domain.', ''],
    ['attributionTitle', 'String', 'Value sent in the X-Keenable-Title attribution header.', ''],
    ['timeout', 'Number', 'Client-side HTTP request timeout in milliseconds.', 'Range: 0-120000. Default: 15000'],
  ]}
/>

## Scrapers

### firecrawlApiKey

<OptionTable
  options={[
    ['firecrawlApiKey', 'String', 'Environment variable name for the Firecrawl API key. If not set in .env, users will be prompted to provide it via UI.', '${FIRECRAWL_API_KEY}'],
  ]}
/>

**Note:** Get your API key from [Firecrawl.dev](https://docs.firecrawl.dev/introduction#api-key)

### firecrawlApiUrl

<OptionTable
  options={[
    ['firecrawlApiUrl', 'String', 'Environment variable name for the Firecrawl API URL. If not set in .env, users will be prompted to provide it via UI.', '${FIRECRAWL_API_URL}'],
  ]}
/>

**Note:** This is optional and only needed if you're using a custom Firecrawl instance.

### firecrawlVersion

<OptionTable
  options={[
    ['firecrawlVersion', 'String', 'Environment variable name for the Firecrawl API version (v0 or v1).', '${FIRECRAWL_VERSION}'],
  ]}
/>

### scraperProvider

<OptionTable
  options={[
    ['scraperProvider', 'String', 'Specifies which scraper service to use.', 'Options: "firecrawl", "serper", "tavily", "keenable"'],
  ]}
/>

### firecrawlOptions

<OptionTable
  options={[
    ['firecrawlOptions', 'Object', 'Advanced configuration options for Firecrawl scraper.', ''],
  ]}
/>

**Subkeys:**

#### formats

<OptionTable
  options={[
    ['formats', 'Array of Strings', 'Formats to include in the output.', ''],
  ]}
/>

#### includeTags

<OptionTable
  options={[
    ['includeTags', 'Array of Strings', 'Tags to include in the output.', ''],
  ]}
/>

#### excludeTags

<OptionTable
  options={[
    ['excludeTags', 'Array of Strings', 'Tags to exclude from the output.', ''],
  ]}
/>

#### headers

<OptionTable
  options={[
    ['headers', 'Object', 'Headers to send with the request. Can be used to send cookies, user-agent, etc.', ''],
  ]}
/>

#### waitFor

<OptionTable
  options={[
    ['waitFor', 'Number', 'Specify a delay in milliseconds before fetching the content, allowing the page sufficient time to load.', ''],
  ]}
/>

#### timeout

<OptionTable
  options={[
    ['timeout', 'Integer', 'Timeout in milliseconds for the scraping request. Must be a non-negative integer.', 'Default: 7500'],
  ]}
/>

#### maxAge

<OptionTable
  options={[
    ['maxAge', 'Number', 'Returns a cached version of the page if it is younger than this age in milliseconds. If a cached version of the page is older than this value, the page will be scraped.', ''],
  ]}
/>

**Note:** If you do not need extremely fresh data, enabling this can speed up your scrapes by 500%.

#### mobile

<OptionTable
  options={[
    ['mobile', 'Boolean', 'Emulate scraping from a mobile device.', ''],
  ]}
/>

#### skipTlsVerification

<OptionTable
  options={[
    ['skipTlsVerification', 'Boolean', 'Skip TLS certificate verification when making requests.', ''],
  ]}
/>

#### blockAds

<OptionTable
  options={[
    ['blockAds', 'Boolean', 'Enables ad-blocking and cookie popup blocking.', ''],
  ]}
/>

#### removeBase64Images

<OptionTable
  options={[
    ['removeBase64Images', 'Boolean', 'Removes all base 64 images from the output, which may be overwhelmingly long. The image\'s alt text remains in the output, but the URL is replaced with a placeholder.', ''],
  ]}
/>

#### parsePDF

<OptionTable
  options={[
    ['parsePDF', 'Boolean', 'Controls how PDF files are processed during scraping.', ''],
  ]}
/>

#### storeInCache

<OptionTable
  options={[
    ['storeInCache', 'Boolean', 'If true, the page will be stored in the Firecrawl index and cache. Setting this to false is useful if your scraping activity may have data protection concerns. Using some parameters associated with sensitive scraping (headers) will force this parameter to be false.', ''],
  ]}
/>

#### zeroDataRetention

<OptionTable
  options={[
    ['zeroDataRetention', 'Boolean', 'If true, this will enable zero data retention for this scrape (requires prior setup on Firecrawl).', ''],
  ]}
/>

#### location

<OptionTable
  options={[
    ['location', 'Object', 'Geographic location and language settings for scraping.', ''],
  ]}
/>

#### onlyMainContent

<OptionTable
  options={[
    ['onlyMainContent', 'Boolean', 'Only return the main content of the page excluding headers, navs, footers, etc.', ''],
  ]}
/>

#### changeTrackingOptions

<OptionTable
  options={[
    ['changeTrackingOptions', 'Object', 'Configuration for tracking changes in scraped content.', ''],
  ]}
/>

**Example:**
```yaml filename="webSearch"
webSearch:
  firecrawlApiKey: "${FIRECRAWL_API_KEY}"
  firecrawlOptions:
    formats: ["markdown", "rawHtml"]
    includeTags: ["main", "article", ".content"]
    excludeTags: ["nav", "footer", ".ads"]
    waitFor: 2000
    timeout: 10000
    mobile: false
    blockAds: true
    onlyMainContent: true
    location:
      country: "US"
      languages: ["en"]
```

**Note:** For detailed information about Firecrawl scraper options and defaults, see the [Firecrawl API Documentation](https://docs.firecrawl.dev/api-reference/endpoint/scrape).

### tavilyScraperOptions

<OptionTable
  options={[
    ['tavilyScraperOptions', 'Object', 'Configuration options for Tavily Extract (scraper).', ''],
  ]}
/>

**Subkeys:**

<OptionTable
  options={[
    ['extractDepth', 'String', 'The depth of the extraction process. "advanced" retrieves more data including tables and embedded content with higher success but may increase latency. "basic" costs 1 credit per 5 successful URLs, "advanced" costs 2 credits per 5 successful URLs.', 'Options: "basic", "advanced". Default: "basic"'],
    ['includeImages', 'Boolean', 'Include a list of images extracted from the URLs in the response.', 'Default: false'],
    ['includeFavicon', 'Boolean', 'Include the favicon URL for each extracted result.', 'Default: false'],
    ['format', 'String', 'The format of the extracted web page content. "markdown" returns content in markdown format. "text" returns plain text and may increase latency.', 'Options: "markdown", "text". Default: "markdown"'],
    ['timeout', 'Number', 'Timeout in milliseconds. Controls the client-side HTTP timeout. When set, it also sends Tavily a server-side extraction timeout converted to seconds and clamped to 1-60s.', 'Default: 15000 for basic, 30000 for advanced'],
  ]}
/>

**Example:**
```yaml filename="webSearch"
webSearch:
  searchProvider: tavily
  scraperProvider: tavily
  tavilyApiKey: "${TAVILY_API_KEY}"
  # Optional: custom Tavily-compatible endpoints
  # tavilySearchUrl: "${TAVILY_SEARCH_URL}"
  # tavilyExtractUrl: "${TAVILY_EXTRACT_URL}"
  tavilySearchOptions:
    searchDepth: basic
    maxResults: 5
    topic: general
  tavilyScraperOptions:
    extractDepth: basic
```

**Note:** For detailed information about Tavily API options, see the [Tavily API Documentation](https://docs.tavily.com).

### keenableScraperOptions

<OptionTable
  options={[
    ['keenableScraperOptions', 'Object', 'Configuration options for Keenable page fetch.', ''],
  ]}
/>

<OptionTable
  options={[
    ['attributionTitle', 'String', 'Value sent in the X-Keenable-Title attribution header.', ''],
    ['timeout', 'Number', 'Client-side HTTP request timeout in milliseconds.', 'Range: 0-120000. Default: 15000'],
  ]}
/>

Keenable can provide search, page fetch, or both. Its public endpoints work without a key; the optional `keenableApiKey` raises rate limits. Set `rerankerType: "none"` to avoid requiring a reranker key:

```yaml filename="webSearch"
webSearch:
  searchProvider: keenable
  scraperProvider: keenable
  rerankerType: none
  # keenableApiKey: "${KEENABLE_API_KEY}" # Optional
  # keenableApiUrl: "${KEENABLE_API_URL}" # Optional search endpoint
  keenableSearchOptions:
    maxResults: 8
    # site: example.com
  keenableScraperOptions:
    timeout: 15000
```

## Rerankers

### jinaApiKey

<OptionTable
  options={[
    ['jinaApiKey', 'String', 'Environment variable name for the Jina API key. If not set in .env, users will be prompted to provide it via UI.', '${JINA_API_KEY}'],
  ]}
/>

**Note:** Get your API key from [Jina.ai](https://jina.ai/api-dashboard/)

### jinaApiUrl

<OptionTable
  options={[
    ['jinaApiUrl', 'String', 'Environment variable name for the Jina API URL. If not set in .env, users will be prompted to provide it via UI.', '${JINA_API_URL}'],
  ]}
/>

**Note:** This is optional and only needed if you're using a custom Jina instance.

### cohereApiKey

<OptionTable
  options={[
    ['cohereApiKey', 'String', 'Environment variable name for the Cohere API key. If not set in .env, users will be prompted to provide it via UI.', '${COHERE_API_KEY}'],
  ]}
/>

**Note:** Get your API key from [Cohere Dashboard](https://dashboard.cohere.com/welcome/login)

### rerankerType

<OptionTable
  options={[
    ['rerankerType', 'String', 'Specifies which reranker service to use. Set to "none" to skip reranking.', 'Options: "jina", "cohere", "none"'],
  ]}
/>

## General Settings

### scraperTimeout

<OptionTable
  options={[
    ['scraperTimeout', 'Integer', 'Timeout in milliseconds for scraper requests. Must be a non-negative integer.', 'Default: 7500'],
  ]}
/>

### safeSearch

<OptionTable
  options={[
    ['safeSearch', 'Number', 'Safe search filtering level. 0 = OFF (no filtering), 1 = MODERATE (default), 2 = STRICT (maximum filtering).', 'Default: 1 (MODERATE)'],
  ]}
/>

**Note:** Safe search levels align with standard search API conventions. MODERATE filtering is enabled by default to provide reasonable content filtering while maintaining search effectiveness. Tavily does not inherit this global setting by default; use `tavilySearchOptions.safeSearch` only if your Tavily account supports `safe_search`.

## Notes

- API keys can be configured in two ways:
  1. Set the environment variables specified in the YAML configuration
  2. If environment variables are not set, users will be prompted to provide the API keys via the UI
- The configuration supports multiple services for each component (providers, scrapers, rerankers)
- If a specific service type is not specified, the system will try all available services in that category
- Safe search provides three levels of content filtering: OFF (0), MODERATE (1), and STRICT (2)
- Tavily does not inherit the global safe search setting by default; set `tavilySearchOptions.safeSearch` explicitly only when your Tavily account supports `safe_search`
- Keenable supports keyless search and page fetch; its optional key raises public rate limits, and its fetch URL override is environment-only
- SearXNG query behavior (engines, language, time range, request timeout) is configured under `searxngSearchOptions`; see [Choosing engines](#choosing-engines) if searches come back empty
- Never put actual API keys in the YAML configuration - only use environment variable names 

## Setting Up SearXNG

SearXNG is a privacy-focused meta search engine that you can self-host. 
For more information, see the [official SearXNG documentation](https://docs.searxng.org/). 

Here are the steps to set up your own SearXNG instance for use with LibreChat:

### Using Docker Desktop

1. **Search for the official SearXNG image**
   - Open Docker Desktop
   - Search for `searxng/searxng` in the **Images** tab
   - Click **Run** on the official image to automatically pull down and run a container of the image

2. **Running the container**
   - Expand the **Optional Settings** dropdown in the proceeding panel that appears once the download completes
   - Set your desired configuration details (port number, container name, etc.)
   - Click **Run** to start the container

3. **Configure SearXNG for LibreChat**
   - Navigate to the `Files` tab in Docker Desktop
   - Go to `/etc/searxng/settings.yaml`
   - Open the file editor
   - Navigate to the `formats` section
   - Add `json` as an acceptable format so that LibreChat can communicate with your instance
   - Save the file

4. **Restart the container**
   - Restart the container for the changes to take effect

#### Video Guide:

Here's a video to guide you through the process from start to finish in about a minute:

     <Video src="/videos/min_searxng_docker_setup_hq.mp4" title="SearXNG Docker setup walkthrough" />

**Note:** In this example, the instance URL is http://localhost:55011 (port number found under the container name in the top left at the end of the video)

### Configuring LibreChat to use SearXNG

You can configure SearXNG in LibreChat within the UI or through `librechat.yaml`.

#### UI Configuration

1. **Open the tools dropdown in the chat input bar**
![Tools configuration button](/images/web-search/tools_highlight.png)

2. **Click on the gear icon next to Web Search**
![Tools configuration section](/images/web-search/tools_config_highlight.png)

3. **Select SearXNG from the Search Provider dropdown**
![SearXNG dropdown selection](/images/web-search/searxng_dropdown_highlight.png)

4. **Enter your configuration details (e.g. instance URL, scraper type, etc.) and click save**
![Save web search configuration](/images/web-search/web_search_save.png)

5. **Click on the Web Search option in the tools dropdown**
![Web search badge in chat interface](/images/web-search/websearch_highlight.png)

6. **The Web Search badge should now be enabled, meaning your queries can now utilize the web search functionality**
![Web search badge confirmation](/images/web-search/search_badge_confirm.png)

#### YAML Configuration

To configure SearXNG for everyone on the instance instead of per user, set the same values in `librechat.yaml`:

```yaml filename="librechat.yaml"
webSearch:
  searchProvider: "searxng"
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
  # searxngApiKey: "${SEARXNG_API_KEY}" # Only if your instance requires authentication
  # Required: a self-hosted instance is a private destination, so exempt it from the connection guard
  allowedAddresses:
    - "localhost:55011"
  searxngSearchOptions:
    engines:
      - google
      - bing
      - startpage
      - qwant
    language: "en"
    timeout: 10000
```

The URL and the key are still read from the environment, so pair the config above with:

```bash filename=".env"
SEARXNG_INSTANCE_URL=http://localhost:55011
# SEARXNG_API_KEY=your_api_key
```

<Callout type="warning" title="Private instances need an allowedAddresses entry">
`localhost:55011` is a loopback address, which LibreChat's [SSRF protection](#ssrf-protection-and-private-providers) blocks at connect time. Without the matching `allowedAddresses` entry the search fails even when SearXNG is healthy. The entry must be the exact `host:port` pair from your instance URL.

When LibreChat itself runs in Docker, `localhost` is the LibreChat container, not the SearXNG one. Use a hostname the container can reach, such as the compose service name, and exempt that instead:

```yaml filename="librechat.yaml"
webSearch:
  searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}" # SEARXNG_INSTANCE_URL=http://searxng:8080
  allowedAddresses:
    - "searxng:8080"
```
</Callout>

See [`searxngSearchOptions`](#searxngsearchoptions) for every subkey this block accepts.

#### Troubleshooting empty results

A SearXNG search that returns nothing is almost always the request never reaching a working instance rather than LibreChat dropping results. Work through these in order:

1. **The instance is a private address with no `allowedAddresses` entry.** LibreChat blocks loopback and private destinations at connect time, so a self-hosted instance needs its exact `host:port` listed under [`allowedAddresses`](#ssrf-protection-and-private-providers). This blocks the request outright rather than returning an empty result set.
2. **`json` is missing from `formats`.** LibreChat requests results as JSON. If the `formats` section of your instance settings file does not list `json`, the instance answers with an error page and every search comes back empty. This is step 3 of the Docker setup above.
3. **Every selected engine failed.** SearXNG merges the engines that did answer, so an empty response means none of them returned anything. DuckDuckGo is in the default set (`google,bing,duckduckgo`) and serves CAPTCHAs to most self-hosted instances, which leaves only two engines to carry the search. Set `searxngSearchOptions.engines` explicitly, leave DuckDuckGo out, and list a few more engines.
4. **An engine name does not exist on your instance.** SearXNG silently skips engines it does not recognize, so a typo just means fewer results. Compare your list against `https://your-instance/config`.
5. **The instance is slower than the timeout.** Raise `searxngSearchOptions.timeout` above the default `10000` if your instance queries many engines or sits behind a slow network path.

You can reproduce what LibreChat sends by calling the instance directly:

```bash
curl -s "http://localhost:55011/search?q=librechat&format=json&engines=google,bing,startpage" | head -c 500
```

This runs from your own machine, so it bypasses LibreChat's connection guard entirely. An empty `results` array here confirms the problem is on the SearXNG side; results here while LibreChat still finds nothing points back at `allowedAddresses`.


# OCR Config Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/ocr)

## Overview

The `ocr` object allows you to configure Optical Character Recognition (OCR) settings for the application, enabling the extraction of text from images. This section provides a detailed breakdown of the `ocr` object structure.

There are 5 main fields under `ocr`:

  - `mistralModel`
  - `apiKey`
  - `baseURL`
  - `strategy`
  - `allowedAddresses`

**Notes:**

- If using the Mistral OCR API, you don't need to edit your `librechat.yaml` file.
    - You only need the following environment variables to get started: `OCR_API_KEY` and `OCR_BASEURL`.
- OCR functionality allows the application to extract text from images, which can then be processed by AI models.
- The default strategy is `mistral_ocr`, which uses Mistral's OCR capabilities.
- You can also configure a custom OCR service by setting the strategy to `custom_ocr`.
- Azure-deployed Mistral OCR models can be used by setting the strategy to `azure_mistral_ocr`.
- Google Vertex AI-deployed Mistral OCR models can be used by setting the strategy to `vertexai_mistral_ocr`.
  - Requires the `GOOGLE_SERVICE_KEY_FILE` environment variable to be set with service account credentials
  - The service key can be provided as: file path, URL, base64 encoded JSON, or raw JSON string
  - Project ID and location are automatically extracted from the service account credentials
- Local text extraction is available via `document_parser`, which extracts text from PDF, DOCX, XLS/XLSX, and OpenDocument files without any external API.
  - Uses `pdfjs-dist`, `mammoth`, and `SheetJS` locally — no API key or base URL needed
  - Only the `strategy` field is required; `apiKey`, `baseURL`, and `mistralModel` are ignored
- If using the default Mistral OCR, you may optionally specify a specific Mistral model to use.
- Environment variable parsing is supported for `apiKey`, `baseURL`, and `mistralModel` parameters.
- A `user_provided` strategy option is planned for future releases but is not yet implemented.

## Automatic Document Parsing (No Configuration Required)

The built-in `document_parser` runs automatically for agent file uploads **even when no `ocr` block is configured** in your `librechat.yaml`. This means PDF, DOCX, XLS/XLSX, and ODS files are parsed out of the box without any setup.

The resolution logic works as follows:

1. **No `ocr` config exists** — When an agent context file is uploaded and its MIME type matches a supported document type (PDF, DOCX, Excel, ODS), the `document_parser` is used directly. No OCR capability check is required for the agent.

2. **`ocr` config exists** — The configured strategy (e.g., `mistral_ocr`) is tried first. If the configured strategy **fails at runtime**, the `document_parser` is used as a fallback so text extraction still succeeds for supported document types.

3. **Neither succeeds** — If both the configured strategy and the document parser fail (e.g., the file is an image-only PDF with no embedded text), an error is returned suggesting that an OCR service is needed.

<Callout type="info">
The `document_parser` handles text-based documents only. For image-based PDFs or scanned documents, you still need a configured OCR strategy (such as `mistral_ocr`) to extract text from the images within those files.
</Callout>

## Example

```yaml filename="ocr"
ocr:
  mistralModel: "mistral-ocr-latest"
  apiKey: "your-mistral-api-key"
  strategy: "mistral_ocr"
```

Example with custom OCR:

```yaml filename="ocr with custom OCR"
ocr:
  apiKey: "your-custom-ocr-api-key"
  baseURL: "https://your-custom-ocr-service.com/api"
  allowedAddresses: ["ocr.internal:8080"]
  strategy: "custom_ocr"
```

Example with Azure Mistral OCR:

```yaml filename="ocr with Azure Mistral OCR"
ocr:
  mistralModel: "deployed-mistral-ocr-2503" # should match deployment name on Azure
  apiKey: "${AZURE_MISTRAL_OCR_API_KEY}" # arbitrary .env var reference
  baseURL: "https://your-deployed-endpoint.models.ai.azure.com/v1" # hardcoded, can also be .env var reference
  strategy: "azure_mistral_ocr"
```

Example with Google Vertex AI Mistral OCR:

```yaml filename="ocr with Google Vertex AI Mistral OCR"
ocr:
  mistralModel: "mistral-ocr-2505" # model name as deployed in Vertex AI
  strategy: "vertexai_mistral_ocr"
```

Example with local document parser (no external API needed):

```yaml filename="ocr with document parser"
ocr:
  strategy: "document_parser"
```

## mistralModel

<OptionTable
  options={[
    ['mistralModel', 'String', 'The Mistral model to use for OCR processing. For Azure deployments, this should match your deployment name. For Google Vertex AI, this should match the model name in your deployment.', 'Optional. Specifies which Mistral model should be used when the strategy is set to mistral_ocr, azure_mistral_ocr, or vertexai_mistral_ocr.'],
  ]}
/>

```yaml filename="ocr / mistralModel"
ocr:
  mistralModel: "mistral-ocr-latest"
```

For Azure deployments:

```yaml filename="ocr / mistralModel (Azure)"
ocr:
  mistralModel: "deployed-mistral-ocr-2503" # Your Azure deployment name
```

For Google Vertex AI deployments:

```yaml filename="ocr / mistralModel (Google Vertex AI)"
ocr:
  mistralModel: "mistral-ocr-2505" # Your Vertex AI model name
```

## apiKey

<OptionTable
  options={[
    ['apiKey', 'String', 'The API key for the OCR service. Not used for Google Vertex AI (uses service account authentication via GOOGLE_SERVICE_KEY_FILE).', 'Optional. Defaults to the environment variable OCR_API_KEY if not specified.'],
  ]}
/>

```yaml filename="ocr / apiKey"
ocr:
  apiKey: "your-ocr-api-key"
```

## baseURL

<OptionTable
  options={[
    ['baseURL', 'String', 'The base URL for the OCR service API. For Google Vertex AI, this is automatically constructed from the service account credentials.', 'Optional. Defaults to the environment variable OCR_BASEURL if not specified.'],
  ]}
/>

```yaml filename="ocr / baseURL"
ocr:
  baseURL: "https://your-ocr-service.com/api"
```

## allowedAddresses

<OptionTable
  options={[
    [
      'allowedAddresses',
      'Array of Strings',
      'Trusted private host:port exemptions for OCR connect-time SSRF checks. Public destinations remain available. Entries require a port and cannot be URLs, paths, CIDR ranges, bare hosts, or public IP literals.',
      'allowedAddresses: ["ocr.internal:8080"]',
    ],
  ]}
/>

## allowedAddresses

OCR requests block private, loopback, link-local, and cloud-metadata destinations at connect time and disable redirects. `allowedAddresses` is an exemption list for exact trusted private `host:port`, `private.ip:port`, or `[ipv6]:port` targets; it does not restrict access to public destinations.

<Callout type="warning" title="Trust exemptions carefully">
  Use `allowedAddresses` only for a private OCR service you control. An exempted hostname trusts whatever private address it resolves to on that port, so prefer a private IP literal when possible. With a forward proxy, the proxy must enforce destination SSRF policy; literal private destinations remain blocked unless explicitly exempted.
</Callout>


## strategy

<OptionTable
  options={[
    ['strategy', 'String', 'The OCR strategy to use.', 'Determines which OCR service to use. Options are "mistral_ocr", "azure_mistral_ocr", "vertexai_mistral_ocr", "document_parser", or "custom_ocr". Defaults to "mistral_ocr".'],
  ]}
/>

```yaml filename="ocr / strategy"
ocr:
  strategy: "custom_ocr"
```

**Available Strategies:**

- `mistral_ocr`: Uses Mistral's OCR capabilities via the standard [Mistral API](/docs/features/ocr#1-mistral-ocr-default).
- `azure_mistral_ocr`: Uses Mistral OCR models deployed on [Azure AI Foundry](/docs/features/ocr#2-azure-mistral-ocr).
- `vertexai_mistral_ocr`: Uses Mistral OCR models deployed on [Google Cloud Vertex AI](/docs/features/ocr#3-google-vertex-ai-mistral-ocr).
- `document_parser`: Uses local text extraction for PDF, DOCX, XLS/XLSX, and OpenDocument files. No external API needed. Also runs automatically for agent file uploads when no `ocr` config is present, and as a fallback when a configured OCR strategy fails.
- `custom_ocr`: Uses a custom OCR service specified by the `baseURL` [(not yet implemented)](/docs/features/ocr#future-enhancements).


# Speech Configuration (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/speech)

## Overview

The `speech` object allows you to configure Text-to-Speech (TTS) and Speech-to-Text (STT) providers directly in your `librechat.yaml` configuration file. This enables server-side speech services without requiring users to configure their own API keys.

**Fields under `speech`:**

- `tts` - Text-to-Speech provider configurations
- `stt` - Speech-to-Text provider configurations
- `speechTab` - Default UI settings for speech features

**Notes:**

- Multiple providers can be configured simultaneously
- Users can select their preferred provider from the available options
- API keys in the config file should use environment variable references for security

## Example

```yaml filename="speech"
speech:
  tts:
    allowedAddresses: ["tts.internal:8080"]
    openai:
      apiKey: "${TTS_API_KEY}"
      model: "tts-1"
      voices: ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
    elevenlabs:
      apiKey: "${ELEVENLABS_API_KEY}"
      model: "eleven_multilingual_v2"
      voices: ["voice-id-1", "voice-id-2"]
  stt:
    allowedAddresses: ["stt.internal:8080"]
    openai:
      apiKey: "${STT_API_KEY}"
      model: "whisper-1"
  speechTab:
    conversationMode: true
    advancedMode: false
    speechToText: true
    textToSpeech: true
```

---

## SSRF Protection

LibreChat guards operator-provided STT and TTS URLs at connect time. Private, loopback, link-local, and cloud-metadata destinations are blocked by default, and redirects are disabled for speech requests.

`speech.tts.allowedAddresses` and `speech.stt.allowedAddresses` are separate exemption lists, not strict allowlists. Add an exact private `host:port`, `private.ip:port`, or `[ipv6]:port` only when that section must reach a trusted self-hosted service. Do not use a URL, path, CIDR range, bare host/IP, or public IP literal. Default ports are normalized, so an HTTPS URL without an explicit port is checked as port `443`.

An exempted hostname trusts whatever private address it resolves to on that port. Prefer a private IP literal when possible, and list only names whose DNS you control and which cannot be repointed by an attacker.

When a forward proxy carries a hostname request, the proxy performs destination DNS and egress, so the proxy must enforce its own SSRF policy. A literal private destination is still blocked before proxy handling unless its exact address and port are exempted.

---

## tts

The `tts` object configures Text-to-Speech providers. Multiple providers can be configured, and users can choose which one to use.

<OptionTable
  options={[
    [
      'allowedAddresses',
      'Array of Strings',
      'Trusted private host:port exemptions for TTS connect-time SSRF checks. Public destinations remain available.',
      'allowedAddresses: ["tts.internal:8080"]',
    ],
  ]}
/>

### openai

OpenAI TTS configuration using models like `tts-1` or `tts-1-hd`.

<OptionTable
  options={[
    ['url', 'String', 'Custom API URL (optional). Use for OpenAI-compatible endpoints.', ''],
    ['apiKey', 'String', 'OpenAI API key. Use environment variable reference.', 'Required'],
    ['model', 'String', 'TTS model to use (e.g., "tts-1", "tts-1-hd").', 'Required'],
    ['voices', 'Array of Strings', 'Available voice options for users to select.', 'Required'],
  ]}
/>

**Example:**
```yaml filename="speech / tts / openai"
tts:
  openai:
    apiKey: "${TTS_API_KEY}"
    model: "tts-1"
    voices: ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
```

### azureOpenAI

Azure OpenAI TTS configuration.

<OptionTable
  options={[
    ['instanceName', 'String', 'Azure OpenAI instance name.', 'Required'],
    ['apiKey', 'String', 'Azure OpenAI API key.', 'Required'],
    ['deploymentName', 'String', 'The deployment name for the TTS model.', 'Required'],
    ['apiVersion', 'String', 'Azure OpenAI API version.', 'Required'],
    ['model', 'String', 'TTS model identifier.', 'Required'],
    ['voices', 'Array of Strings', 'Available voice options.', 'Required'],
  ]}
/>

**Example:**
```yaml filename="speech / tts / azureOpenAI"
tts:
  azureOpenAI:
    instanceName: "my-azure-instance"
    apiKey: "${AZURE_TTS_API_KEY}"
    deploymentName: "tts-deployment"
    apiVersion: "2024-02-15-preview"
    model: "tts-1"
    voices: ["alloy", "echo", "nova"]
```

### elevenlabs

ElevenLabs TTS configuration for high-quality voice synthesis.

<OptionTable
  options={[
    ['url', 'String', 'Custom API URL (optional).', ''],
    ['websocketUrl', 'String', 'WebSocket URL for streaming (optional).', ''],
    ['apiKey', 'String', 'ElevenLabs API key.', 'Required'],
    ['model', 'String', 'ElevenLabs model (e.g., "eleven_multilingual_v2").', 'Required'],
    ['voices', 'Array of Strings', 'Voice IDs available for selection.', 'Required'],
    ['voice_settings', 'Object', 'Voice customization settings (optional).', ''],
    ['pronunciation_dictionary_locators', 'Array of Strings', 'Pronunciation dictionary IDs (optional).', ''],
  ]}
/>

**voice_settings Sub-keys:**
<OptionTable
  options={[
    ['similarity_boost', 'Number', 'Voice similarity enhancement (0-1).', ''],
    ['stability', 'Number', 'Voice stability (0-1).', ''],
    ['style', 'Number', 'Style exaggeration (0-1).', ''],
    ['use_speaker_boost', 'Boolean', 'Enable speaker boost.', ''],
  ]}
/>

**Example:**
```yaml filename="speech / tts / elevenlabs"
tts:
  elevenlabs:
    apiKey: "${ELEVENLABS_API_KEY}"
    model: "eleven_multilingual_v2"
    voices: ["21m00Tcm4TlvDq8ikWAM", "AZnzlk1XvdvUeBnXmlld"]
    voice_settings:
      stability: 0.5
      similarity_boost: 0.75
      use_speaker_boost: true
```

### localai

LocalAI TTS configuration for self-hosted speech synthesis.

<OptionTable
  options={[
    ['url', 'String', 'LocalAI server URL.', 'Required'],
    ['apiKey', 'String', 'API key if authentication is enabled (optional).', ''],
    ['voices', 'Array of Strings', 'Available voice models.', 'Required'],
    ['backend', 'String', 'TTS backend to use (e.g., "piper").', 'Required'],
  ]}
/>

**Example:**
```yaml filename="speech / tts / localai"
tts:
  localai:
    url: "http://localhost:8080"
    voices: ["en-us-amy-low", "en-us-danny-low"]
    backend: "piper"
```

---

## stt

The `stt` object configures Speech-to-Text providers.

<OptionTable
  options={[
    [
      'allowedAddresses',
      'Array of Strings',
      'Trusted private host:port exemptions for STT connect-time SSRF checks. Public destinations remain available.',
      'allowedAddresses: ["stt.internal:8080"]',
    ],
  ]}
/>

### openai

OpenAI Whisper STT configuration.

<OptionTable
  options={[
    ['url', 'String', 'Custom API URL (optional). Use for OpenAI-compatible endpoints.', ''],
    ['apiKey', 'String', 'OpenAI API key. Use environment variable reference.', 'Required'],
    ['model', 'String', 'STT model to use (e.g., "whisper-1").', 'Required'],
  ]}
/>

**Example:**
```yaml filename="speech / stt / openai"
stt:
  openai:
    apiKey: "${STT_API_KEY}"
    model: "whisper-1"
```

### azureOpenAI

Azure OpenAI Whisper STT configuration.

<OptionTable
  options={[
    ['instanceName', 'String', 'Azure OpenAI instance name.', 'Required'],
    ['apiKey', 'String', 'Azure OpenAI API key.', 'Required'],
    ['deploymentName', 'String', 'The deployment name for the Whisper model.', 'Required'],
    ['apiVersion', 'String', 'Azure OpenAI API version.', 'Required'],
  ]}
/>

**Example:**
```yaml filename="speech / stt / azureOpenAI"
stt:
  azureOpenAI:
    instanceName: "my-azure-instance"
    apiKey: "${AZURE_STT_API_KEY}"
    deploymentName: "whisper-deployment"
    apiVersion: "2024-02-15-preview"
```

---

## speechTab

The `speechTab` object configures default UI settings for speech features. These settings control what users see by default in the speech settings panel.

<OptionTable
  options={[
    ['conversationMode', 'Boolean', 'Enable conversation mode by default.', 'false'],
    ['advancedMode', 'Boolean', 'Show advanced speech settings by default.', 'false'],
    ['speechToText', 'Boolean or Object', 'Enable STT by default, or configure detailed STT settings.', 'false'],
    ['textToSpeech', 'Boolean or Object', 'Enable TTS by default, or configure detailed TTS settings.', 'false'],
  ]}
/>

### speechToText (Object format)

When using an object instead of a boolean:

<OptionTable
  options={[
    [
      'engineSTT',
      'String',
      'Default STT engine. Use `"browser"` or `"external"`. Legacy `"openai"` and `"azureOpenAI"` values are accepted and normalized to `"external"`.',
      'browser',
    ],
    ['languageSTT', 'String', 'Default language for STT.', ''],
    ['autoTranscribeAudio', 'Boolean', 'Keep the microphone listening instead of stopping at the first pause. With an external engine it also turns on silence detection based on `decibelValue`.', ''],
    ['decibelValue', 'Number', 'Decibel threshold for silence detection. Range -100 to -30, default -45.', ''],
    ['autoSendText', 'Number', 'Seconds to wait after transcription before auto-sending. `0` sends immediately; `-1` disables auto-send.', ''],
  ]}
/>

### textToSpeech (Object format)

When using an object instead of a boolean:

<OptionTable
  options={[
    [
      'engineTTS',
      'String',
      'Default TTS engine. Use `"browser"` or `"external"`. Legacy provider values are accepted and normalized to `"external"`.',
      'browser',
    ],
    ['voice', 'String', 'Default voice selection.', ''],
    ['languageTTS', 'String', 'Default language for TTS.', ''],
    ['automaticPlayback', 'Boolean', 'Automatically play TTS responses.', ''],
    ['playbackRate', 'Number', 'Default playback speed (1.0 = normal). Range: 0.25–4.0.', ''],
    ['cacheTTS', 'Boolean', 'Cache TTS audio for repeated playback.', ''],
  ]}
/>

**Example:**
```yaml filename="speech / speechTab"
speechTab:
  conversationMode: false
  advancedMode: false
  speechToText:
    engineSTT: "external"
    autoTranscribeAudio: true
    decibelValue: -45
  textToSpeech:
    engineTTS: "external"
    voice: "nova"
    automaticPlayback: false
    playbackRate: 1.0
    cacheTTS: true
```

---

## Complete Example

```yaml filename="librechat.yaml"
version: 1.3.15
cache: true

speech:
  tts:
    openai:
      apiKey: "${TTS_API_KEY}"
      model: "tts-1-hd"
      voices: ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
    elevenlabs:
      apiKey: "${ELEVENLABS_API_KEY}"
      model: "eleven_multilingual_v2"
      voices: ["21m00Tcm4TlvDq8ikWAM", "AZnzlk1XvdvUeBnXmlld"]
      voice_settings:
        stability: 0.5
        similarity_boost: 0.75
  stt:
    openai:
      apiKey: "${STT_API_KEY}"
      model: "whisper-1"
  speechTab:
    conversationMode: false
    advancedMode: false
    speechToText: true
    textToSpeech:
      engineTTS: "external"
      voice: "nova"
      automaticPlayback: false
```

---

## Notes

- Always use environment variable references (e.g., `${API_KEY}`) for API keys in configuration files
- Multiple TTS providers can be configured; users select their preferred option in the UI
- The `speechTab` settings define defaults that users can override in their personal settings
- `browser` uses the browser's built-in speech support; `external` uses the server-side providers configured under `speech.stt` or `speech.tts`
- Existing `openai` and `azureOpenAI` STT defaults, and `openai`, `azureOpenAI`, `elevenlabs`, and `localai` TTS defaults, are migrated to `external`; an unavailable external engine falls back to `browser`
- For detailed feature documentation, see [Speech to Text & Text to Speech](/docs/configuration/stt_tts)


# CloudFront Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/cloudfront)

The `cloudfront` object configures CloudFront delivery for files stored in S3. It is required when `fileStrategy` or any `fileStrategies` entry uses `"cloudfront"`.

## Example

```yaml filename="cloudfront"
fileStrategies:
  avatar: 'cloudfront'
  image: 'cloudfront'
  document: 's3'

cloudfront:
  domain: 'https://cdn.example.com'
  distributionId: 'E1234ABCD'
  invalidateOnDelete: false
  imageSigning: 'cookies'
  cookieDomain: '.example.com'
  cookieExpiry: 1800
  urlExpiry: 3600
  storageRegion: 'us-east-2'
  includeRegionInPath: false
  requireSignedAccess: true
```

## Fields

<OptionTable
  options={[
    [
      'domain',
      'String',
      'CloudFront distribution domain or CNAME. Required.',
      'domain: "https://cdn.example.com"',
    ],
    [
      'distributionId',
      'String',
      'CloudFront distribution ID. Required when `invalidateOnDelete` is true.',
      'distributionId: "E1234ABCD"',
    ],
    [
      'invalidateOnDelete',
      'Boolean',
      'Creates a CloudFront invalidation after deleting the S3 object. Default: false.',
      'invalidateOnDelete: false',
    ],
    [
      'imageSigning',
      'String',
      'Inline media signing mode. Options: `"none"`, `"cookies"`, `"url"`. `"url"` is reserved and not implemented for images.',
      'imageSigning: "cookies"',
    ],
    [
      'urlExpiry',
      'Number',
      'Signed CloudFront download URL lifetime in seconds. Default: 3600.',
      'urlExpiry: 3600',
    ],
    [
      'cookieExpiry',
      'Number',
      'Signed cookie lifetime in seconds. Default: 1800. Maximum: 604800.',
      'cookieExpiry: 1800',
    ],
    [
      'cookieDomain',
      'String',
      'Shared parent domain for signed cookies. Required when `imageSigning` is `"cookies"`. Must start with a dot.',
      'cookieDomain: ".example.com"',
    ],
    [
      'storageRegion',
      'String',
      'Optional region label used in generated object keys when `includeRegionInPath` is true.',
      'storageRegion: "us-east-2"',
    ],
    [
      'includeRegionInPath',
      'Boolean',
      'Includes the storage region in newly generated object keys. Default: false.',
      'includeRegionInPath: false',
    ],
    [
      'requireSignedAccess',
      'Boolean',
      'Requires signed-cookie CloudFront access to initialize successfully at startup. Default: false.',
      'requireSignedAccess: true',
    ],
  ]}
/>

## Validation Rules

- `distributionId` is required when `invalidateOnDelete` is `true`.
- `cookieDomain` is required when `imageSigning` is `"cookies"`.
- `cookieDomain` must start with a dot, for example `.example.com`.
- `requireSignedAccess: true` requires `imageSigning: "cookies"`.

## Related Environment Variables

<OptionTable
  options={[
    [
      'CLOUDFRONT_KEY_PAIR_ID',
      'String',
      'CloudFront public key pair ID. Required for signed cookies and signed downloads.',
      '# CLOUDFRONT_KEY_PAIR_ID=K1234567890ABC',
    ],
    [
      'CLOUDFRONT_PRIVATE_KEY',
      'String',
      'CloudFront private key PEM. Required for signed cookies and signed downloads.',
      '# CLOUDFRONT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----"',
    ],
  ]}
/>

For deployment guidance, see [CloudFront with S3](/docs/configuration/cdn/cloudfront).


# File Config Object Structure (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/file_config)

## Overview

The `fileConfig` object allows you to configure file handling settings for the application, including size limits and MIME type restrictions. This section provides a detailed breakdown of the `fileConfig` object structure.

There are 8 main fields under `fileConfig`:

  - `endpoints`
  - `serverFileSizeLimit`
  - `avatarSizeLimit`
  - `imageGeneration`
  - `fileTokenLimit`
  - `ocr`
  - `text`
  - `stt`

**Notes:**

- At the time of writing, the Assistants endpoint [supports filetypes from this list](https://platform.openai.com/docs/assistants/tools/file-search#supported-files).
- OpenAI, Azure OpenAI, Google, and Custom endpoints support files through the [RAG API.](../../rag_api.mdx)
- OpenAI, Azure OpenAI, Anthropic, Google, and AWS Bedrock endpoints support direct file uploads via [Upload to Provider](/docs/features/ocr#5-upload-files-to-provider-direct).
- The `ocr`, `text`, and `stt` sections control file processing for features like [Upload as Text](/docs/features/upload_as_text) and [OCR](/docs/features/ocr)
- Any other endpoints not mentioned, like Plugins, do not support file uploads (yet).
- The Assistants endpoint has a defined endpoint value of `assistants`. All other endpoints use the defined value `default`
  - For non-assistants endpoints, you can adjust file settings for all of them under `default`
  - If you'd like to adjust settings for a specific endpoint, you can list their corresponding endpoint names:
    - `assistants`
        - does not use "default" as it has defined defaults separate from the others.
    - `openAI`
    - `azureOpenAI`
    - `google`
    - `bedrock`
    - `YourCustomEndpointName`
- You can omit values, in which case, the app will use the default values as defined per endpoint type listed below.
- LibreChat counts 1 megabyte as follows: `1 x 1024 x 1024`
- Long-running upload response streaming and server-side remote URL fetch limits are configured with the [`FILE_UPLOAD_SSE_ENABLED`, `REMOTE_FILE_FETCH_TIMEOUT_MS`, and `REMOTE_FILE_FETCH_MAX_BYTES` environment variables](/docs/configuration/dotenv#file-uploads).

## Example

```yaml filename="fileConfig"
fileConfig:
  endpoints:
    assistants:
      fileLimit: 5
      fileSizeLimit: 10
      totalSizeLimit: 50
      supportedMimeTypes:
        - "image/.*"
        - "application/pdf"
    openAI:
      disabled: true
    default:
      totalSizeLimit: 20
    YourCustomEndpointName:
      fileLimit: 5
      fileSizeLimit: 1000
      supportedMimeTypes:
        - "image/.*"
  serverFileSizeLimit: 1000
  avatarSizeLimit: 2
  fileTokenLimit: 100000
  imageGeneration:
    percentage: 100
    px: 1024
  ocr:
    supportedMimeTypes:
      - "^image/(jpeg|gif|png|webp|heic|heif)$"
      - "^application/pdf$"
      - "^application/vnd\\.openxmlformats-officedocument\\.(wordprocessingml\\.document|presentationml\\.(presentation|template)|spreadsheetml\\.sheet)$"
      - "^application/vnd\\.ms-(word|powerpoint|excel)$"
      - "^application/epub\\+zip$"
  text:
    supportedMimeTypes:
      - "^text/(plain|markdown|csv|json|xml|html|css|javascript|typescript|x-python|x-java|x-csharp|x-php|x-ruby|x-go|x-rust|x-kotlin|x-swift|x-scala|x-perl|x-lua|x-shell|x-sql|x-yaml|x-toml)$"
  stt:
    supportedMimeTypes:
      - "^audio/(mp3|mpeg|mpeg3|wav|wave|x-wav|ogg|vorbis|mp4|x-m4a|flac|x-flac|webm)$"
```

<Callout type="info" title="MIME pattern syntax">
  LibreChat evaluates administrator-configured `supportedMimeTypes` patterns on the server with an RE2-compatible, linear-time regular expression engine. Backreferences and lookaround are not supported. Invalid patterns are logged and skipped; if every pattern in a configured list is invalid, that list fails closed and rejects every file instead of becoming unrestricted.
</Callout>

## serverFileSizeLimit

<OptionTable
  options={[
    ['serverFileSizeLimit', 'Integer', 'The global maximum size for any file uploaded to the server, specified in megabytes (MB).', 'Acts as an overarching limit for file uploads across all endpoints, ensuring that no file exceeds this size server-wide.'],
  ]}
/>

```yaml filename="fileConfig / serverFileSizeLimit"
fileConfig:
  serverFileSizeLimit: 1000
```

## avatarSizeLimit

<OptionTable
options={[
['avatarSizeLimit', 'Integer', 'The maximum size allowed for avatar images, specified in megabytes (MB).', 'Specifically tailored for user avatar uploads, allowing for control over image sizes to maintain consistent quality and loading times.'],
]}
/>

```yaml filename="fileConfig / avatarSizeLimit"
fileConfig:
  avatarSizeLimit: 2
```

## imageGeneration

<OptionTable
  options={[
    ['imageGeneration', 'Object', 'Settings related to image generation output quality and dimensions.', 'Allows configuration of either output size as a percentage relative to some base size or as an explicit pixel dimension.'],
  ]}
/>

`imageGeneration` supports the following parameters:

- `percentage` (Integer)
  - The output size of the generated image expressed as a percentage (e.g., `100` means 100% of base size).
  - Use this to scale the output image relative to a default or original size.

- `px` (Integer)
  - Specifies the output image dimension in pixels (e.g., `1024`).
  - Use this to explicitly set the output size of the generated image regardless of base size.

You may set only one of these parameters (`percentage` or `px`), not both, depending on your use case

Example configuration:

```yaml filename="fileConfig / imageGeneration"
fileConfig:
  imageGeneration:
    percentage: 100
    px: 1024
```

## fileTokenLimit

<OptionTable
  options={[
    ['fileTokenLimit', 'Number', 'Maximum number of tokens from text files to include in prompts before truncation.', 'fileTokenLimit: 100000'],
  ]}
/>

**Description:** When attaching text content, LibreChat truncates the text at runtime to the configured token limit just before prompt construction.

**Default:** `100000`

```yaml filename="fileConfig / fileTokenLimit"
fileConfig:
  fileTokenLimit: 100000
```

## ocr

<OptionTable
  options={[
    ['ocr', 'Object', 'Settings for Optical Character Recognition (OCR) file processing.', 'Configures which file types are processed using OCR.'],
  ]}
/>

**Description:** The `ocr` section configures which file types should be processed using OCR functionality for extracting text from visual documents.

**Note:** This section controls file type matching for OCR processing. To enable agent capabilities and configure OCR services, see:
- [Agents Configuration](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) for the `ocr` and `context` capabilities
- [OCR Configuration](/docs/configuration/librechat_yaml/object_structure/ocr) for OCR service setup

### supportedMimeTypes

<OptionTable
  options={[
    ['supportedMimeTypes', 'Array of Strings', 'List of MIME type patterns for files that should be processed with OCR.', 'Uses regular expressions to match file types.'],
  ]}
/>

**Default:** Images, PDFs, and Office documents

```yaml filename="fileConfig / ocr / supportedMimeTypes"
fileConfig:
  ocr:
    supportedMimeTypes:
      - "^image/(jpeg|gif|png|webp|heic|heif)$"
      - "^application/pdf$"
      - "^application/vnd\\.openxmlformats-officedocument\\.(wordprocessingml\\.document|presentationml\\.(presentation|template)|spreadsheetml\\.sheet)$"
      - "^application/vnd\\.ms-(word|powerpoint|excel)$"
      - "^application/epub\\+zip$"
```

## text

<OptionTable
  options={[
    ['text', 'Object', 'Settings for direct text file parsing without OCR.', 'Configures which file types are processed as plain text files for direct content extraction.'],
  ]}
/>

**Description:** The `text` section configures which file types should be processed using direct text extraction.

**Note:** Text parsing is the default method used by the "Upload as Text" feature (controlled by the `context` capability). It first attempts to use the text parsing library from the RAG API, and if the RAG API is not connected, it falls back to a simpler text extraction method without requiring any external services. See [Upload as Text](/docs/features/upload_as_text) for more information.

### supportedMimeTypes

<OptionTable
  options={[
    ['supportedMimeTypes', 'Array of Strings', 'List of MIME type patterns for files that should be parsed as plain text.', 'Uses regular expressions to match file types.'],
  ]}
/>

**Default:** All text files, common programming languages, and `.eml` email files

```yaml filename="fileConfig / text / supportedMimeTypes"
fileConfig:
  text:
    supportedMimeTypes:
      - "^text/(plain|markdown|csv|json|xml|html|css|javascript|typescript|x-python|x-java|x-csharp|x-php|x-ruby|x-go|x-rust|x-kotlin|x-swift|x-scala|x-perl|x-lua|x-shell|x-sql|x-yaml|x-toml)$"
```

## stt

<OptionTable
  options={[
    ['stt', 'Object', 'Settings for Speech-to-Text (STT) audio file processing.', 'Configures which audio file types are processed using STT for transcription.'],
  ]}
/>

**Description:** The `stt` section configures which audio file types should be processed using Speech-to-Text functionality for converting audio to text.

### supportedMimeTypes

<OptionTable
  options={[
    ['supportedMimeTypes', 'Array of Strings', 'List of MIME type patterns for audio files that should be transcribed with STT.', 'Uses regular expressions to match audio file types.'],
  ]}
/>

**Default:** Common audio formats

```yaml filename="fileConfig / stt / supportedMimeTypes"
fileConfig:
  stt:
    supportedMimeTypes:
      - "^audio/(mp3|mpeg|mpeg3|wav|wave|x-wav|ogg|vorbis|mp4|x-m4a|flac|x-flac|webm)$"
```

**Notes:**
- Files matching `text` patterns are processed with simple text extraction
- Files matching `ocr` patterns are processed with the provided OCR service
- Files matching `stt` patterns are processed with Speech-to-Text transcription
- **Processing precedence: OCR > STT > text parsing > fallback**
- Files not matching any pattern will fall back to text parsing

## File Processing Priority

LibreChat processes uploaded files based on MIME type matching with the following **priority order**:

1. **OCR** - If file matches `ocr.supportedMimeTypes` AND OCR is configured
2. **STT** - If file matches `stt.supportedMimeTypes` AND STT is configured
3. **Text Parsing** - If file matches `text.supportedMimeTypes`
4. **Fallback** - Text parsing as last resort

This processing order ensures optimal extraction quality while maintaining functionality even when specialized services (OCR/STT) are not configured.

### Processing Examples

**PDF file with OCR configured:**
- File matches `ocr.supportedMimeTypes`
- **Uses OCR** to extract text
- Better quality for scanned PDFs and images

**PDF file without OCR configured:**
- File matches `text.supportedMimeTypes` (or uses fallback)
- **Uses text parsing** library
- Works well for digital PDFs with selectable text

**Python file:**
- File matches `text.supportedMimeTypes`
- **Uses text parsing** (no OCR needed)
- Direct text extraction

**Audio file with STT configured:**
- File matches `stt.supportedMimeTypes`
- **Uses STT** to transcribe audio to text

**Image file without OCR configured:**
- File matches `ocr.supportedMimeTypes` but OCR not available
- **Falls back to text parsing**
- Limited extraction capability without OCR

This priority system allows features like "Upload as Text" to work without requiring OCR configuration, while still leveraging OCR when available for improved extraction quality.

## endpoints

<OptionTable
  options={[
    ['endpoints', 'Record/Object', 'Configures file handling settings for individual endpoints, allowing customization per endpoint basis.', 'Specifies file handling configurations for individual endpoints, allowing customization per endpoint basis.'],
  ]}
/>

**Description:** Each object under endpoints is a record that can have the following settings:

### Overview

  - `disabled`
      - Whether file handling is disabled for the endpoint.
  - `fileLimit`
      - The maximum number of files allowed per upload request.
  - `fileSizeLimit`
      - The maximum size for a single file. In units of MB (e.g. use `20` for 20 megabytes)
  - `totalSizeLimit`
      - The total maximum size for all files in a single request. In units of MB (e.g. use `20` for 20 megabytes)
  - `supportedMimeTypes`
      - A list of [Regular Expressions](https://en.wikipedia.org/wiki/Regular_expression) specifying what MIME types are allowed for upload. This can be customized to restrict file types.

## disabled

<OptionTable
  options={[
    ['disabled', 'Boolean', 'Indicates whether file uploading is disabled for a specific endpoint.', 'Setting this to `true` prevents any file uploads to the specified endpoint, overriding any other file-related settings.'],
  ]}
/>

**Default:** `false`

```yaml filename="fileConfig / endpoints / {endpoint_record} / disabled"
openAI:
  disabled: true
```

## fileLimit

**Key:**
<OptionTable
  options={[
    ['fileLimit', 'Integer', 'The maximum number of files allowed in a single upload request.', 'Helps control the volume of uploads and manage server load.'],
  ]}
/>

**Default:** Varies by endpoint

```yaml filename="fileConfig / endpoints / {endpoint_record} / fileLimit"
assistants:
  fileLimit: 5
```

## fileSizeLimit

**Key:**
<OptionTable
  options={[
    ['fileSizeLimit', 'Integer', 'The maximum size allowed for each individual file, specified in megabytes (MB).', 'This limit ensures that no single file exceeds the specified size, allowing for better resource allocation and management.'],
  ]}
/>

**Default:** Varies by endpoint

```yaml filename="fileConfig / endpoints / {endpoint_record} / fileSizeLimit"
YourCustomEndpointName:
  fileSizeLimit: 1000
```

## totalSizeLimit

**Key:**
<OptionTable
  options={[
    ['totalSizeLimit', 'Integer', 'The total maximum size allowed for all files in a single request, specified in megabytes (MB).', 'This setting is crucial for preventing excessive bandwidth and storage usage by any single upload request.'],
  ]}
/>

**Default:** Varies by endpoint

```yaml filename="fileConfig / endpoints / {endpoint_record} / totalSizeLimit"
assistants:
  totalSizeLimit: 50
```

## supportedMimeTypes

**Key:**
<OptionTable
  options={[
    ['supportedMimeTypes', 'Array of Strings', 'A list of regular expressions defining the MIME types permitted for upload.', 'This allows for precise control over the types of files that can be uploaded. Invalid regex is ignored.'],
  ]}
/>

**Default:** Varies by endpoint

```yaml filename="fileConfig / endpoints / {endpoint_record} / supportedMimeTypes"
assistants:
  supportedMimeTypes:
      - "image/.*"
      - "application/pdf"
```

LibreChat normalizes common MIME aliases before applying these patterns. In particular, `application/x-shellscript` (reported by Chrome on Linux) and `text/x-shellscript` (reported by libmagic) become `application/x-sh`. A custom allowlist that accepts shell scripts should therefore permit the canonical `application/x-sh` type. Rejected uploads return the reported unsupported type to the client instead of a generic server error.


# Transactions (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/transactions)

## Overview

The `transactions` object controls whether token usage records are saved to the database in LibreChat. This allows administrators to enable or disable transaction tracking independently from the balance system.

<Callout type="info" title="This setting only controls storage, not display">

`transactions` decides whether usage records are **written to the database**. It does not surface token counts anywhere in the app, so turning it on alone will not make token information appear in the UI.

To *see* token usage, use `interface.contextUsage`, which draws the in-conversation context gauge and is on by default. `interface.contextCost` is a separate setting, off by default, that adds cost figures to that gauge; leave it off if you want token counts without exposing pricing. See [Token Usage](/docs/configuration/token_usage#viewing-context-usage-and-cost). To enforce per-user credit limits, see [Balance](/docs/configuration/librechat_yaml/object_structure/balance).

</Callout>

**Fields under `transactions`:**

- `enabled`

**Notes:**

- Transaction recording is essential for tracking historical token usage
- When `balance.enabled` is set to `true`, transactions are automatically enabled regardless of this setting
- Default value is `true` to ensure token usage is tracked by default
- Disabling transactions can reduce database storage requirements but will prevent historical usage analysis

## Example

```yaml filename="transactions"
transactions:
  enabled: false
```

## enabled

**Key:**

<OptionTable
    options={[
        ['enabled', 'Boolean', 'Controls whether to save transaction records to the database.', 'Default: true. Set to false to disable transaction recording (unless balance.enabled is true).'],
    ]}
/>

**Description:**

The `enabled` field determines whether LibreChat saves detailed transaction records for each token usage event. These records include:

- Token counts for prompts and completions
- Associated costs and rates
- User and conversation identifiers
- Timestamps for each transaction

**Important Behavior:**

When the balance system is enabled (`balance.enabled: true`), transaction recording is automatically enabled regardless of the `transactions.enabled` setting. This ensures that:

1. Balance tracking functions correctly with a complete audit trail
2. Token usage can be accurately calculated and deducted from user balances
3. Historical data is available for balance reconciliation

**Use Cases:**

- **Enable transactions** (`true`): When you need to track usage patterns, generate reports, or maintain an audit trail
- **Disable transactions** (`false`): When you want to reduce database storage and don't need historical usage data (only works when balance tracking is also disabled)

## Relationship with Balance System

The transactions and balance systems work together:

```yaml filename="Example: Transactions with Balance"
# When balance is enabled, transactions are always enabled
balance:
  enabled: true
  startBalance: 20000

transactions:
  enabled: false  # This will be overridden to true because balance.enabled is true
```

```yaml filename="Example: Standalone Transaction Tracking"
# Track transactions without balance management
balance:
  enabled: false

transactions:
  enabled: true  # Records all token usage without enforcing balance limits
```

## Database Impact

When transactions are enabled, each API call that consumes tokens creates a record in the "Transactions" collection with the following information:

- User ID and email
- Conversation ID
- Model used
- Token counts (prompt and completion)
- Token values and rates
- Timestamp
- Transaction type (credit or debit)

Consider the storage implications when enabling transactions for high-volume deployments.

# Balance (https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/balance)

## Overview

The `balance` object allows administrators to configure how token credit balances are managed for users within LibreChat. Settings include enabling balance tracking, initializing user balances, and configuring automatic token refill behavior.

**Fields under `balance`:**

- `enabled`
- `startBalance`
- `autoRefillEnabled`
- `refillIntervalValue`
- `refillIntervalUnit`
- `refillAmount`

**Notes:**

- `balance` configurations apply globally across the application.
- Defaults are provided but can be customized based on requirements.
- Conditional logic can dynamically modify these settings based on other configurations.


## Example

```yaml filename="balance"
balance:
  enabled: false
  startBalance: 20000
  autoRefillEnabled: false
  refillIntervalValue: 30
  refillIntervalUnit: "days"
  refillAmount: 10000
```


## enabled

**Key:**

<OptionTable
    options={[
        ['enabled', 'Boolean', 'Enables token credit tracking and balance management for users.', 'Set to true to activate balance tracking for token usage.'],
    ]}
/>

**Default:** `false`

**Example:**

```yaml filename="balance / enabled"
balance:
  enabled: true
```


## startBalance

**Key:**

<OptionTable
    options={[
        ['startBalance', 'Integer', 'Specifies the initial number of tokens credited to a user upon registration.', 'Tokens credited to a new user account.'],
    ]}
/>

**Default:** `20000`

**Example:**

```yaml filename="balance / startBalance"
balance:
  startBalance: 20000
```


## autoRefillEnabled

**Key:**

<OptionTable
    options={[
        ['autoRefillEnabled', 'Boolean', 'Determines whether automatic refilling of token credits is enabled.', 'Set to true to enable automatic token refills.'],
    ]}
/>

**Default:** `false`

**Example:**

```yaml filename="balance / autoRefillEnabled"
balance:
  autoRefillEnabled: true
```


## refillIntervalValue

**Key:**

<OptionTable
    options={[
        ['refillIntervalValue', 'Integer', 'Specifies the numerical value for the interval at which token credits are automatically refilled.', 'For example, 30 represents a 30-day interval.'],
    ]}
/>

**Default:** `30`

**Example:**

```yaml filename="balance / refillIntervalValue"
balance:
  refillIntervalValue: 30
```


## refillIntervalUnit

**Key:**

<OptionTable
    options={[
        ['refillIntervalUnit', 'String', 'Specifies the time unit for the refill interval (e.g., "days", "hours").', 'Indicates the unit of time for refillIntervalValue.'],
    ]}
/>

**Default:** `"days"`

**Example:**

```yaml filename="balance / refillIntervalUnit"
balance:
  refillIntervalUnit: "days"
```


## refillAmount

**Key:**

<OptionTable
    options={[
        ['refillAmount', 'Integer', 'Specifies the number of tokens to be added to the user\'s balance during each automatic refill.', 'The amount added to a user’s token credits at each refill interval.'],
    ]}
/>

**Default:** `10000`

**Example:**

```yaml filename="balance / refillAmount"
balance:
  refillAmount: 10000
```

# Authentication System (https://www.librechat.ai/docs/configuration/authentication)

## General

For a quick overview, refer to the user guide provided here: [Authentication](/docs/features/authentication)

Here's an overview of the general configuration.

<OptionTable
  options={[
    [
      'ALLOW_EMAIL_LOGIN',
      'boolean',
      'Show email login and allow local or LDAP credential login through the authentication API.',
      'ALLOW_EMAIL_LOGIN=true',
    ],
    [
      'ALLOW_EMAIL_LOGIN_OVERRIDE',
      'boolean',
      'Allow direct credential requests to the login API while ALLOW_EMAIL_LOGIN is false. Default: false.',
      'ALLOW_EMAIL_LOGIN_OVERRIDE=false',
    ],
    [
      'ALLOW_REGISTRATION',
      'boolean',
      'Enable or disable email registration of new users.',
      'ALLOW_REGISTRATION=true',
    ],
    [
      'ALLOW_SOCIAL_LOGIN',
      'boolean',
      'Allow users to connect to LibreChat with various social networks.',
      'ALLOW_SOCIAL_LOGIN=false',
    ],
    [
      'ALLOW_SOCIAL_REGISTRATION',
      'boolean',
      'Enable or disable registration of new users using various social networks.',
      'ALLOW_SOCIAL_REGISTRATION=false',
    ],
  ]}
/>

> **Note:** OpenID and SAML do not support the ability to disable only registration.

Setting `ALLOW_EMAIL_LOGIN=false` hides the email login form and rejects local or LDAP credential requests to `/api/auth/login`; OAuth, OpenID Connect, and SAML sign-in are unaffected. `ALLOW_EMAIL_LOGIN_OVERRIDE=true` is intended only for a controlled API integration that still needs credential login while the form is hidden. Every override use logs the request IP, so protect and monitor that route carefully.

Quick Tips:

- Even with registration disabled, you can add users directly to the database using [the create-user script](#create-user-script) detailed below.
- To delete a user, you can use [the delete-user script](#delete-user-script) also detailed below.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/4c51dc25-31d3-4c51-8c2a-0cdfb5a25033"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/3bc5371d-e51d-4e91-ac68-56db6e85bb2c"
  alt="User registration screen"
/>

## Session Expiry and Refresh Token

- Default values: session expiry: 15 minutes, refresh token expiry: 7 days
  - For more information: **[GitHub PR #927 - Refresh Token](https://github.com/danny-avila/LibreChat/pull/927)**

<OptionTable
  options={[
    ['SESSION_EXPIRY', 'integer (milliseconds)', 'Session expiry time.','SESSION_EXPIRY=1000 * 60 * 15'],
    ['REFRESH_TOKEN_EXPIRY', 'integer (milliseconds)', 'Refresh token expiry time.','REFRESH_TOKEN_EXPIRY=(1000 * 60 * 60 * 24) * 7'],
  ]}
/>

``` mermaid
sequenceDiagram
    Client->>Server: Login request with credentials
    Server->>Passport: Use authentication strategy (e.g., 'local', 'google', etc.)
    Passport-->>Server: User object or false/error
    Note over Server: If valid user...
    Server->>Server: Generate access and refresh tokens
    Server->>Database: Store hashed refresh token
    Server-->>Client: Access token and refresh token
    Client->>Client: Store access token in HTTP Header and refresh token in HttpOnly cookie
    Client->>Server: Request with access token from HTTP Header
    Server-->>Client: Requested data
    Note over Client,Server: Access token expires
    Client->>Server: Request with expired access token
    Server-->>Client: Unauthorized
    Client->>Server: Request with refresh token from HttpOnly cookie
    Server->>Database: Retrieve hashed refresh token
    Server->>Server: Compare hash of provided refresh token with stored hash
    Note over Server: If hashes match...
    Server-->>Client: New access token and refresh token
    Client->>Server: Retry request with new access token
    Server-->>Client: Requested data
```

## JWT Secret and Refresh Secret

Use unique values of at least 32 bytes. Generate permanent values with the [Credentials Generator](/toolkit/creds_generator), store them securely, and provide the same values to every LibreChat replica.

<OptionTable
  options={[
    ['JWT_SECRET', 'string (hex)', 'JWT secret key.','JWT_SECRET='],
    ['JWT_REFRESH_SECRET', 'string (hex)', 'JWT refresh secret key.','JWT_REFRESH_SECRET='],
  ]}
/>

When either value is blank, LibreChat can generate and reuse a value from its temporary credentials file. The default Docker Compose stacks persist that file, but this is intended only as a bootstrap convenience. If the file is lost or cannot be written, sessions can become invalid after restart. See [Credentials Configuration](/docs/configuration/dotenv#credentials-configuration) for precedence, persistence, and production guidance.

---

## Automated Moderation System (optional)

The Automated Moderation System is enabled by default. It uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.

To set up the mod system, review [the setup guide](/docs/configuration/mod_system).

> *Please Note: If you want this to work in development mode, you will need to create a file called `.env.development` in the root directory and set `DOMAIN_CLIENT` to `http://localhost:3090` or whatever port  is provided by vite when runnning `npm run frontend-dev`*

## User Management Scripts

### Create User Script

The create-user script allows you to add users directly to the database, even when registration is disabled. Here's how to use it:

1. For the default `docker-compose.yml` (if you use `docker compose up` to start the app):
   ```bash
   docker compose exec api npm run create-user
   ```

2. For the `deploy-compose.yml` (if you followed the [Ubuntu Docker Guide](/docs/remote/docker_linux)):
   ```bash
   docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run create-user"
   ```

3. For local development (from project root):
   ```bash
   npm run create-user
   ```

Follow the prompts to enter the new user's email and password.

### Delete User Script

To delete a user, you can use the delete-user script:

1. For the default `docker-compose.yml` (if you use `docker compose up` to start the app):
   ```bash
   docker compose exec api npm run delete-user email@domain.com
   ```

2. For the `deploy-compose.yml` (if you followed the [Ubuntu Docker Guide](/docs/remote/docker_linux)):
   ```bash
   docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run delete-user email@domain.com"
   ```

3. For local development (from project root):
   ```bash
   npm run delete-user email@domain.com
   ```

Replace `email@domain.com` with the email of the user you want to delete.


# Email setup (https://www.librechat.ai/docs/configuration/authentication/email)

For a quick overview, refer to the user guide provided here: [Password Reset](/docs/features/password_reset)

## General setup

LibreChat supports multiple email providers:
- **Mailgun API** - Recommended for servers that block SMTP ports
- **SMTP Services** - Traditional email sending via Gmail, Outlook, or custom mail servers

### Common Configuration

These variables are used by both Mailgun and SMTP:

<OptionTable
  options={[
    ['EMAIL_FROM', 'string', 'From email address. Required.','EMAIL_FROM=noreply@librechat.ai'],
    ['EMAIL_FROM_NAME', 'string', 'From name (defaults to APP_TITLE if not set).','EMAIL_FROM_NAME=LibreChat'],
  ]}
/>

### Mailgun Configuration (Recommended)

Mailgun is particularly useful for deployments on servers that block SMTP ports to prevent spam. When both `MAILGUN_API_KEY` and `MAILGUN_DOMAIN` are set, LibreChat will use Mailgun instead of SMTP.

<OptionTable
  options={[
    ['MAILGUN_API_KEY', 'string', 'Your Mailgun API key (required for Mailgun).','MAILGUN_API_KEY='],
    ['MAILGUN_DOMAIN', 'string', 'Your Mailgun domain, e.g., mg.yourdomain.com (required for Mailgun).','MAILGUN_DOMAIN='],
    ['MAILGUN_HOST', 'string', 'Custom Mailgun API host (optional). Use https://api.eu.mailgun.net for EU region.','MAILGUN_HOST=https://api.mailgun.net'],
  ]}
/>

### SMTP Configuration

**Basic Configuration**

If you want to use one of the predefined services, configure only these variables:
For more info about supported email services: https://nodemailer.com/smtp/well-known-services

<OptionTable
  options={[
    ['EMAIL_SERVICE', 'string', 'Email service (e.g., Gmail, Outlook).','EMAIL_SERVICE='],
    ['EMAIL_USERNAME', 'string', 'Username for authentication.','EMAIL_USERNAME='],
    ['EMAIL_PASSWORD', 'string', 'Password for authentication.','EMAIL_PASSWORD='],
  ]}
/>

**Advanced Configuration**

If you want to use a generic SMTP service or need advanced configuration for one of the predefined providers, configure these variables as well:

<OptionTable
  options={[
    ['EMAIL_HOST', 'string', 'Mail server host.','EMAIL_HOST='],
    ['EMAIL_PORT', 'number', 'Mail server port.','EMAIL_PORT=25'],
    ['EMAIL_ENCRYPTION', 'string', 'Encryption method (starttls, tls, etc.).','EMAIL_ENCRYPTION='],
    ['EMAIL_ENCRYPTION_HOSTNAME', 'string', 'Hostname for encryption.','EMAIL_ENCRYPTION_HOSTNAME='],
    ['EMAIL_ALLOW_SELFSIGNED', 'boolean', 'Allow self-signed certificates.','EMAIL_ALLOW_SELFSIGNED='],
  ]}
/>

<Callout type="warning" title="Warning">
**Failing to configure either Mailgun or SMTP properly will result in LibreChat using the unsecured password reset! This allows anyone to reset any password on your server immediately, without mail being sent at all!**
</Callout>

## Setup with Mailgun

To set up Mailgun, follow these steps:

1. Sign up for a Mailgun account at [mailgun.com](https://www.mailgun.com/)
2. Add and verify your domain in the Mailgun dashboard
3. Navigate to the API Keys section and copy your Private API key
4. In the `.env` file, modify the variables as follows:

<OptionTable
  options={[    
    ['MAILGUN_API_KEY', 'string', 'Your Mailgun private API key', 'MAILGUN_API_KEY=your-mailgun-api-key'],
    ['MAILGUN_DOMAIN', 'string', 'Your verified Mailgun domain', 'MAILGUN_DOMAIN=mg.yourdomain.com'],
    ['EMAIL_FROM', 'string', 'Sender email address', 'EMAIL_FROM=noreply@yourdomain.com'],
    ['EMAIL_FROM_NAME', 'string', 'Sender name', 'EMAIL_FROM_NAME=LibreChat'],
    ['MAILGUN_HOST', 'string', '(Optional) For EU region', 'MAILGUN_HOST=https://api.eu.mailgun.net'],
  ]}
/>

<Callout type="info" title="Note">
If your Mailgun account is in the EU region, make sure to set `MAILGUN_HOST=https://api.eu.mailgun.net`
</Callout>

## Setup with Gmail

To set up Gmail, follow these steps:

1. Create a Google Account and enable 2-step verification.
2. In the **[Google Account settings](https://myaccount.google.com/)**, click on the "Security" tab and open "2-step verification."
3. Scroll down and open "App passwords." Choose "Mail" for the app and select "Other" for the device, then give it a random name.
4. Click on "Generate" to create a password, and copy the generated password.
5. In the `.env` file, modify the variables as follows:

<OptionTable
  options={[    
    ['EMAIL_SERVICE', 'string', 'gmail', 'EMAIL_SERVICE=gmail'],
    ['EMAIL_USERNAME', 'string', 'your-email', 'EMAIL_USERNAME=your-email'],
    ['EMAIL_PASSWORD', 'string', 'your-email-password', 'EMAIL_PASSWORD=your-email-password'],
    ['EMAIL_FROM', 'string', 'email address for the from field, e.g., noreply@librechat.ai', 'EMAIL_FROM=noreply@librechat.ai'],
    ['EMAIL_FROM_NAME', 'string', 'My LibreChat Server', 'EMAIL_FROM_NAME=LibreChat'],
  ]}
/>

## Setup with custom mail server

To set up a custom mail server, follow these steps:

1. Gather your SMTP login data from your provider. The steps are different for each, but they will usually list values for all variables.
2. In the `.env` file, modify the variables as follows, assuming some sensible example values:

<OptionTable
  options={[    
    ['EMAIL_HOST', 'string', 'Hostname to connect to', 'EMAIL_HOST=mail.example.com'],
    ['EMAIL_PORT', 'integer', 'Port to connect to','EMAIL_PORT=25'],
    ['EMAIL_ENCRYPTION', 'string', 'Encryption type','EMAIL_ENCRYPTION=starttls'],
    ['EMAIL_USERNAME', 'string', 'Your email username','EMAIL_USERNAME=usernale@example.com'],
    ['EMAIL_PASSWORD', 'string', 'Your app password','EMAIL_PASSWORD=password'],
    ['EMAIL_FROM', 'string', 'Email address for the from field','EMAIL_FROM=noreply@librechat.ai'],
    ['EMAIL_FROM_NAME', 'string', 'Name that will appear in the "from" field','EMAIL_FROM_NAME=LibreChat'],
  ]}
/>

## Complete Configuration Examples

### Example 1: Mailgun Configuration

```bash
# ===================================
# Email Configuration - Mailgun
# ===================================
# Mailgun is recommended for servers that block SMTP ports

# Required Mailgun settings
MAILGUN_API_KEY=your-mailgun-api-key
MAILGUN_DOMAIN=mg.yourdomain.com

# Optional: For EU region
# MAILGUN_HOST=https://api.eu.mailgun.net

# Common email settings
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME=LibreChat

# Enable password reset functionality
ALLOW_PASSWORD_RESET=true
```

### Example 2: Gmail SMTP Configuration

```bash
# ===================================
# Email Configuration - Gmail SMTP
# ===================================
# Traditional SMTP configuration

# Gmail service configuration
EMAIL_SERVICE=gmail
EMAIL_USERNAME=your-email@gmail.com
EMAIL_PASSWORD=your-app-password

# Common email settings
EMAIL_FROM=your-email@gmail.com
EMAIL_FROM_NAME=LibreChat

# Enable password reset functionality
ALLOW_PASSWORD_RESET=true
```

### Example 3: Custom SMTP Server Configuration

```bash
# ===================================
# Email Configuration - Custom SMTP
# ===================================
# For custom mail servers

# SMTP server details
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_ENCRYPTION=starttls
EMAIL_USERNAME=username@example.com
EMAIL_PASSWORD=your-password

# Optional settings
# EMAIL_ENCRYPTION_HOSTNAME=
# EMAIL_ALLOW_SELFSIGNED=false

# Common email settings
EMAIL_FROM=noreply@example.com
EMAIL_FROM_NAME=LibreChat

# Enable password reset functionality
ALLOW_PASSWORD_RESET=true
```

## Troubleshooting

### Mailgun Issues

1. **Authentication Failed**: Ensure your Mailgun API key is correct and has sending permissions
2. **Domain Not Found**: Verify your Mailgun domain is correctly configured in your Mailgun account
3. **EU Region Issues**: If your Mailgun account is in the EU region, make sure to set `MAILGUN_HOST=https://api.eu.mailgun.net`
4. **Fallback to SMTP**: If only one of `MAILGUN_API_KEY` or `MAILGUN_DOMAIN` is set, the system will fall back to SMTP configuration

### SMTP Issues

1. **Connection Refused**: Check if your server allows outbound SMTP connections on the specified port
2. **Authentication Failed**: Verify your username and password are correct
3. **Gmail App Password**: For Gmail, you must use an app-specific password, not your regular password
4. **Self-signed Certificates**: If your mail server uses self-signed certificates, set `EMAIL_ALLOW_SELFSIGNED=true`

### General Issues

1. **No Emails Sent**: Check the LibreChat logs for error messages
2. **Unsecured Password Reset**: This occurs when neither Mailgun nor SMTP is properly configured
3. **From Address Issues**: Ensure the `EMAIL_FROM` address is valid and authorized to send from your mail service


# LDAP/AD (https://www.librechat.ai/docs/configuration/authentication/ldap)

You can use a Lightweight Directory Access Protocol (LDAP) authentication server to authenticate users.

## LDAP/AD Server Configuration

**Basic Configuration**

- `LDAP_URL` and `LDAP_USER_SEARCH_BASE` are required.
- `LDAP_SEARCH_FILTER` is optional; if not specified, the `mail` attribute is used by default. If specified, use the literal `{{username}}` to use the given username for the search.

<OptionTable
  options={[
    ['LDAP_URL', 'string', 'LDAP server URL.', 'LDAP_URL=ldap://localhost:389'],
    ['LDAP_BIND_DN', 'string', 'Bind DN', 'LDAP_BIND_DN=cn=root'],
    ['LDAP_BIND_CREDENTIALS', 'string', 'Password for bindDN', 'LDAP_BIND_CREDENTIALS=password'],
    [
      'LDAP_USER_SEARCH_BASE',
      'string',
      'LDAP user search base',
      'LDAP_USER_SEARCH_BASE=o=users,o=example.com',
    ],
    ['LDAP_SEARCH_FILTER', 'string', 'LDAP search filter', 'LDAP_SEARCH_FILTER=mail={{username}}'],
  ]}
/>

**Field Mappings**

You can specify a mapping between the attributes of LibreChat users and those of LDAP users. Use these settings if the default mappings do not work properly.

<OptionTable
  options={[
    [
      'LDAP_ID',
      'string',
      'Specify a unique user ID. By default, uid or sAMAccountName, mail is used.',
      'LDAP_ID=uid',
    ],
    [
      'LDAP_USERNAME',
      'string',
      'By default, it uses givenName or mail.',
      'LDAP_USERNAME=givenName',
    ],
    [
      'LDAP_EMAIL',
      'string',
      'By default, it uses mail.',
      'LDAP_EMAIL=userPrincipalName',
    ],
    [
      'LDAP_FULL_NAME',
      'string',
      'By default, it uses a combination of givenName and surname.',
      'LDAP_FULL_NAME=givenName,surname',
    ],
  ]}
/>

**Username or Email**

By default, LibreChat uses an email address and password for authentication.
This may sometimes cause problem with LDAP and you may want to use a username instead.
Set the `LDAP_SEARCH_FILTER` to filter for the username instead (e.g. `LDAP_SEARCH_FILTER=uid={{username}}`
and configure LibreChat to request login via username:

<OptionTable
  options={[
    [
      'LDAP_LOGIN_USES_USERNAME',
      'string',
      'Use username instead of email.',
      'LDAP_LOGIN_USES_USERNAME=true',
    ],
  ]}
/>

**Active Directory over SSL**

To connect via SSL (ldaps://), such as a company using Windows AD, specify the path to the internal CA certificate.
`LDAP_TLS_REJECT_UNAUTHORIZED` is optional;if not specified LibreChat will reject TLS/SSL connections if the LDAP server's certificate cannot be verified.
set `LDAP_TLS_REJECT_UNAUTHORIZED` to false (not recommended for production environments)
to allow Librechat to accept TLS/SSL connections even if the LDAP server's certificate cannot be verified,

<OptionTable
  options={[
    [
      'LDAP_CA_CERT_PATH',
      'string',
      'CA certificate path.',
      'LDAP_CA_CERT_PATH=/path/to/root_ca_cert.crt',
    ],
    [
      'LDAP_TLS_REJECT_UNAUTHORIZED',
      'string',
      'Disable TLS verification',
      'LDAP_TLS_REJECT_UNAUTHORIZED=true',
    ],
  ]}
/>

**LDAP StartTLS**

Enabling LDAP StartTLS allows LibreChat to upgrade an insecure connection to a secure TLS connection. This is useful if you want to secure the connection without switching to ldaps://.

<OptionTable
    options={[
        [
            'LDAP_STARTTLS',
            'string',
            'Enable LDAP StartTLS for upgrading the connection to TLS. Set to true to enable this feature.',
            'LDAP_STARTTLS=true',
        ],
    ]}
/>


# Overview (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC)

This section will cover how to configure OAuth2 and OpenID Connect with LibreChat

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/786fa525-73c4-4640-b4cf-91925ad8802e"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/dddc34c6-9602-4177-89e8-4c0db01b0eac"
  alt="OAuth2 and OpenID Connect login screen"
/>

## OAuth2

- [Apple](/docs/configuration/authentication/OAuth2-OIDC/apple)
- [Discord](/docs/configuration/authentication/OAuth2-OIDC/discord)
- [Facebook](/docs/configuration/authentication/OAuth2-OIDC/facebook)
- [GitHub](/docs/configuration/authentication/OAuth2-OIDC/github)
- [Google](/docs/configuration/authentication/OAuth2-OIDC/google)

## OpenID Connect

- [Auth0](/docs/configuration/authentication/OAuth2-OIDC/auth0)
- [AWS Cognito](/docs/configuration/authentication/OAuth2-OIDC/aws)
- [Azure Entra/AD](/docs/configuration/authentication/OAuth2-OIDC/azure)
- [Keycloak](/docs/configuration/authentication/OAuth2-OIDC/keycloak)
- [Re-use OpenID Tokens for Login Session](/docs/configuration/authentication/OAuth2-OIDC/token-reuse)

### OpenID JWT User Cache

High-request-rate deployments can set `AUTH_USER_CACHE_MODE=on` to cache the user document resolved for OpenID JWT authentication for five seconds. This reduces repeated database reads during short request bursts without extending the authentication token lifetime.

The cache is off by default and requires `USE_REDIS=true` with the `AUTH_USER_DOC` namespace backed by Redis. LibreChat disables the mode and logs a warning when those requirements are not met. Cached documents are sanitized before storage and invalidated when the user is updated.

```bash filename=".env"
USE_REDIS=true
AUTH_USER_CACHE_MODE=on
```

## Troubleshooting OpenID Connect

If you encounter issues with OpenID Connect authentication:

1. **Enable Header Debug Logging**: Set `DEBUG_OPENID_REQUESTS=true` in your environment variables to log request headers in addition to URLs (with sensitive data masked). Note: Request URLs are always logged at debug level
2. **Check Redirect URIs**: Ensure your callback URL matches exactly between your provider and LibreChat configuration
3. **Verify Scopes**: Make sure all required scopes are properly configured
4. **Review Provider Logs**: Check your identity provider's logs for authentication errors
5. **Validate Tokens**: Ensure your provider is issuing valid tokens with the expected claims
6. **Ensure _nonce_ is generated**: Some identity providers generate `nonce` url parameter if it's missing in the request. Set `OPENID_GENERATE_NONCE=true` to force the openid-client to generate it.

### Admin Panel Redirects

If the Admin Panel is hosted on a separate URL from LibreChat, set [`ADMIN_PANEL_URL`](/docs/features/admin_panel#librechat-redirect-url) in the LibreChat API environment. This tells LibreChat where to send admins after the admin OAuth or SSO callback completes.


# Apple (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/apple)

## Prerequisites

Before you begin, ensure you have the following:

- **Apple Developer Account:** If you don't have one, enroll [here](https://developer.apple.com/programs/enroll/).

---

## Creating a New App ID

### 1. Log in to the Apple Developer Console

- **Action:**
- Visit [Apple Developer](https://developer.apple.com/) and sign in with your Apple ID.


### 2. Navigate to Identifiers

- Go to **Certificates, Identifiers & Profiles**.
- Click on **Identifiers** in the sidebar.


### 3. Create a New App ID

1. Click the **"+"** button to add a new identifier.
2. Select **App IDs** and click **Continue**.
3. Choose **App** and click **Continue**.
4. Enter a **Description** for your App ID (e.g., `LibreChat App ID`).
5. Set the **Bundle ID** (e.g., `com.yourdomain.librechat`).
6. Click **Continue** and then **Register**.

- **Image References:**
- ![Create App ID](https://user-images.githubusercontent.com/5569219/59017558-6d643600-8861-11e9-927b-a4952b56f34e.png)
*Figure 1: Creating a New App ID*

- ![Select App](https://github.com/user-attachments/assets/7b67c1bb-dea0-4475-ad45-e3c13ad514d5)
*Figure 2: Selecting App Identifier*

### 4. Enable "Sign in with Apple"

1. After creating the App ID, click on it to edit.
2. Under **Capabilities**, find and check **Sign in with Apple**.
3. Click **Save**.

- **Image Reference:**
- ![Enable Sign in with Apple](https://user-images.githubusercontent.com/5569219/59017720-dea3e900-8861-11e9-898e-f486c093edd8.png)
*Figure 3: Enabling "Sign in with Apple"*

---

## Creating a Services ID

### 1. Navigate to Identifiers

- In the **Certificates, Identifiers & Profiles** section, click on **Identifiers**.

### 2. Create a New Services ID

1. Click the **"+"** button.
2. Select **Services IDs** and click **Continue**.
3. Enter a **Description** (e.g., `LibreChat Services ID`).
4. Enter an **Identifier** (e.g., `com.yourdomain.librechat.services`).
5. Click **Continue** and then **Register**.

- **Image References:**
- ![Select Services ID](https://user-images.githubusercontent.com/5569219/59017808-16ab2c00-8862-11e9-8beb-4da7bb509b0c.png)
*Figure 4: Selecting Services ID*

- ![Create Services ID](https://github.com/user-attachments/assets/cac99e43-a6d7-4fb8-890d-eabd87a60e7d)
*Figure 5: Creating Services ID*

### 3. Configure "Sign in with Apple"

1. Click on the newly created Services ID.
2. Under **Capabilities**, click **Configure** next to **Sign in with Apple**.
3. Enter your **Domains** (e.g., `your-domain.com`) and **Return URLs** (e.g., `https://your-domain.com/oauth/apple/callback`).
4. Click **Next** and then **Register**.

- **Image Reference:**
- ![Configure Sign in with Apple](https://github.com/user-attachments/assets/9309e1c1-6f98-49fc-a87d-bb5f46e200f7)
*Figure 6: Configuring "Sign in with Apple" for Services ID*

- ![Web Authentication Configuration](https://github.com/user-attachments/assets/d1ca9ad2-e555-4083-a974-b26239c9694f)
*Figure 7: Web Authentication Configuration*

- ![Web Authentication Configuration](https://github.com/user-attachments/assets/8facac9e-002f-458b-8049-63c91afc30de)
*Figure 8: Save edit Services ID Configuration*


---

## Creating a Key

### 1. Navigate to Keys

- In the **Certificates, Identifiers & Profiles** section, click on **Keys**.

### 2. Create a New Key

1. Click the **"+"** button to add a new key.
2. Enter a **Key Name** (e.g., `LibreChatSignInWithApple`).
3. Select **Sign in with Apple** under **Capabilities**.
4. Click **Configure** and select the created App ID (e.g., `com.yourdomain.librechat`), then click **Save**.
5. Click **Continue** and then **Register**.

- **Image References:**
- ![Create Key](https://github.com/user-attachments/assets/6db095dd-79dd-485d-a57a-8b1ea523b67f)
*Figure 8: Creating a New Key*

- ![Configure Key](https://github.com/user-attachments/assets/30b593ab-b8f3-4d94-b56d-1eeac1900a1f)
*Figure 9: Configuring the Key with App ID*

- ![Register a New Key](https://github.com/user-attachments/assets/91aa4d6e-9b5c-4f7c-bb1a-5980015ee15d)
*Figure 10: Registering the Key*

### 3. Download the Private Key

1. After creating the key, click **Download**.
2. **Important:** Save the `.p8` file securely. You will not be able to download it again.
3. Note the **Key ID**; you'll need it for the `.env` file.

- **Image Reference:**
- ![Download Your Key](https://github.com/user-attachments/assets/79e5aefa-9797-4fa7-bdf9-cb4b526ab3dd)
*Figure 11: Downloading the Private Key*

---

## Configuring LibreChat

### 1. Update `.env` Configuration

Add the following Apple OAuth2 configuration to your `.env` file:

```env filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

# Apple
APPLE_CLIENT_ID=com.yourdomain.librechat.services
APPLE_TEAM_ID=YOUR_TEAM_ID
APPLE_KEY_ID=YOUR_KEY_ID
APPLE_PRIVATE_KEY_PATH=/path/to/AuthKey.p8 # Absolute path to your downloaded .p8 file
APPLE_CALLBACK_URL=/oauth/apple/callback
```

> **Note:** 
> - Replace `com.yourdomain.librechat.services` with your actual Services ID.
> - Replace `YOUR_TEAM_ID` and `YOUR_KEY_ID` with the respective values from your Apple Developer account.
> - If using Docker, ensure the `.p8` file is accessible within your Docker container and update the `APPLE_PRIVATE_KEY_PATH` accordingly.

### 2. Restart LibreChat

After updating the `.env` file, restart LibreChat to apply the changes.

- **If using Docker:**

```bash
docker compose up -d
  ```

---

## Troubleshooting

If you encounter issues during the setup, consider the following solutions:

- **Invalid Redirect URI:**
    - Ensure that the redirect URI in your Apple Developer Console (`https://your-domain.com/oauth/apple/callback`) matches exactly with the one specified in your `.env` file (`APPLE_CALLBACK_URL`).

- **Private Key Issues:**
    - Verify that the path to your `.p8` file (`APPLE_PRIVATE_KEY_PATH`) is correct.
    - Ensure that LibreChat has read permissions for the `.p8` file.

- **Team ID and Key ID Errors:**
    - Double-check that the `APPLE_TEAM_ID` and `APPLE_KEY_ID` in your `.env` file match those in your Apple Developer Account.

- **Domain Verification Failed:**
    - Ensure that the verification file is correctly uploaded to the root of your domain.
    - Verify that there are no typos in the domain name entered during configuration.

- **Docker Configuration Issues:**
    - If using Docker, confirm that the `.p8` file is properly mounted and the path in `APPLE_PRIVATE_KEY_PATH` is accessible within the container.

- **Check Logs:**
    - Review LibreChat logs for any error messages related to Apple authentication. This can provide specific insights into what might be going wrong.


# Discord (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/discord)

## Create a new Discord Application

- Go to **[Discord Developer Portal](https://discord.com/developers)**

- Create a new Application and give it a name

![image](https://github.com/danny-avila/LibreChat/assets/32828263/7e7cdfa0-d1d6-4b6b-a8a9-905aaa40d135)

## Discord Application Configuration

- In the OAuth2 general settings add a valid redirect URL:
    - Example for localhost: `http://localhost:3080/oauth/discord/callback`
    - Example for a domain: `https://example.com/oauth/discord/callback`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/6c56fb92-f4ab-43b9-981b-f98babeeb19d)

- In `Default Authorization Link`, select `In-app Authorization` and set the scopes to `applications.commands`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/2ce94670-9422-48d2-97e9-ec40bd331573)

- Save changes and reset the Client Secret

![image](https://github.com/danny-avila/LibreChat/assets/32828263/3af164fc-66ed-4e5e-9f5a-9bcab3df37b4)
![image](https://github.com/danny-avila/LibreChat/assets/32828263/2ece3935-68e6-4f2e-8656-9721cba5388a)

## .env Configuration

- Paste your `Client ID` and `Client Secret` in the `.env` file:

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret
DISCORD_CALLBACK_URL=/oauth/discord/callback
```

- Save the `.env` file

> Note: If using docker, run `docker compose up -d` to apply the .env configuration changes


# Facebook (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/facebook)

> ⚠️ **Warning: Work in progress, not currently functional**

> ❗ Note: Facebook Authentication will not work from `localhost`

## Create a Facebook Application

- Go to the **[Facebook Developer Portal](https://developers.facebook.com/)**

- Click on "My Apps" in the header menu

![image](https://github.com/danny-avila/LibreChat/assets/32828263/b75ccb8b-d56b-41b7-8b0d-a32c2e762962)

- Create a new application

![image](https://github.com/danny-avila/LibreChat/assets/32828263/706f050d-5423-44cc-80f0-120913695d8f)

- Select "Authenticate and request data from users with Facebook Login"

![image](https://github.com/danny-avila/LibreChat/assets/32828263/2ebbb571-afe8-429e-ab39-be6e83d12c01)

- Choose "No, I'm not creating a game"

![image](https://github.com/danny-avila/LibreChat/assets/32828263/88b5160a-9c72-414a-bbcc-7717b81106f3)

- Provide an `app name` and `App contact email` and click `Create app`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/e1282c9e-4e7d-4cbe-82c9-cc76967f83e1)

## Facebook Application Configuration

- In the side menu, select "Use cases" and click "Customize" under "Authentication and account creation."

![image](https://github.com/danny-avila/LibreChat/assets/32828263/39f4bb70-d9dc-4d1c-8443-2666fe56499b)

-  Add the `email permission`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/dfa20879-2cb8-4daf-883d-3790854afca0)

- Now click `Go to settings`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/512213a2-bd8b-4fd3-96c7-0de6d3222ddd)

- Ensure that `Client OAuth login`, `Web OAuth login` and `Enforce HTTPS` are **enabled**.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/3a7d935b-97bf-493b-b909-39ecf9b3432b)

- Add a `Valid OAuth Redirect URIs` and "Save changes"
    - Example for a domain: `https://example.com/oauth/facebook/callback`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/ef8e54ee-a766-4871-9719-d4eff7a770b6)

- Click `Go back` and select `Basic` in the `App settings` tab

![image](https://github.com/danny-avila/LibreChat/assets/32828263/0d14f702-5183-422e-a12c-5d1b6031581b)

- Click "Show" next to the App secret.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/9a009e37-2bb6-4da6-b5c7-9139c3db6185)

## .env Configuration

- Copy the `App ID` and `App Secret` and paste them into the `.env` file as follows:

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

FACEBOOK_CLIENT_ID=your_app_id
FACEBOOK_CLIENT_SECRET=your_app_secret
FACEBOOK_CALLBACK_URL=/oauth/facebook/callback
```

- Save the `.env` file.

> Note: If using docker, run `docker compose up -d` to apply the .env configuration changes


# GitHub (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/github)

## Create a GitHub Application

- Go to your **[Github Developer settings](https://github.com/settings/apps)**
- Create a new GitHub app

![image](https://github.com/danny-avila/LibreChat/assets/138638445/3a8b88e7-78f8-426e-bfc2-c5e3f8b21ccb)

## GitHub Application Configuration

-  Give it a `GitHub App name` and set your `Homepage URL`
    - Example for localhost: `http://localhost:3080`
    - Example for a domain: `https://example.com`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/f10d497d-460b-410f-9504-08735662648b)

- Add a valid `Callback URL`:
    - Example for localhost: `http://localhost:3080/oauth/github/callback`
    - Example for a domain: `https://example.com/oauth/github/callback`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/4e7e6dba-0afb-4ed8-94bf-4c61b0f29240)

- Uncheck the box labeled `Active` in the `Webhook` section

![image](https://github.com/danny-avila/LibreChat/assets/138638445/aaeb3ecb-2e76-4ea5-8264-edfbdd53de1a)

- Scroll down to `Account permissions` and set `Email addresses` to `Access: Read-only`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/3e561aa4-1f9e-4cb7-ace8-dbba8f0c0d55)

![image](https://github.com/danny-avila/LibreChat/assets/138638445/7b5f99af-7bde-43ee-9b43-6d3ce79ee00a)

- Click on `Create GitHub App`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/4cc48550-eac3-4970-939b-81a23fa9c7cf)

## .env Configuration

- Click `Generate a new client secret`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/484c7851-71dd-4167-a59e-9a56c4e08c36)

- Copy the `Client ID` and `Client Secret` in the `.env` file

![image](https://github.com/danny-avila/LibreChat/assets/138638445/aaf78840-48a9-44e1-9625-4109ed91d965)

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
GITHUB_CALLBACK_URL=/oauth/github/callback

# GitHub Enterprise (optional)
# Uncomment and configure the following if you are using GitHub Enterprise for authentication
# GITHUB_ENTERPRISE_BASE_URL=https://your-ghe-instance.com
# GITHUB_ENTERPRISE_USER_AGENT=YourEnterpriseAppName
```

- Save the `.env` file

> **Note:** If using Docker, run `docker compose up -d` to apply the .env configuration changes

# Google (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/google)

## Create a Google Application

- Visit: **[Google Cloud Console](https://cloud.google.com)** and open the `Console`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/a7d290ea-6031-43b3-b367-36ce00e46f20)

- Create a New Project and give it a name

![image](https://github.com/danny-avila/LibreChat/assets/138638445/ce71c9ca-7ddd-4021-9133-a872c64c20c4)

![image](https://github.com/danny-avila/LibreChat/assets/138638445/8abbd41e-8332-4851-898d-9cddb373c527)

## Google Application Configuration

- Select the project you just created and go to `APIs and Services`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/c6265582-2cf6-430f-ae51-1edbdd9f2c48)

![image](https://github.com/danny-avila/LibreChat/assets/138638445/006e16ba-56b8-452d-b324-5f2d202637ab)

- Select `Credentials` and click `CONFIGURE CONSENT SCREEN`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/e4285cbb-833f-4366-820d-addf04a2ad77)

- Select `External` then click `CREATE`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/232d46c0-dd00-4637-b538-3ba3bdbdc0b2)

- Fill in your App information

> Note: You can get a logo from your LibreChat folder here: `docs\assets\favicon_package\android-chrome-192x192.png`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/e6c4c8ec-2f02-4af5-9458-c72394d0b7c5)

- Configure your `App domain` and add your `Developer contact information` then click `SAVE AND CONTINUE`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/6c2aa557-9b9b-412d-bc2b-76a0dc11f394)

- Configure the `Sopes`
    - Add `email`,`profile` and `openid`
    - Click `UPDATE` and `SAVE AND CONTINUE`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/46af2fb9-8cfd-41c5-a763-814b308e45c3)

![image](https://github.com/danny-avila/LibreChat/assets/138638445/4e832970-d392-4c67-bb38-908a5c51660a)

- Click `SAVE AND CONTINUE`
- Review your app and go back to dashboard

- Go back to the `Credentials` tab, click on `+ CREATE CREDENTIALS` and select `OAuth client ID`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/beef1982-55a3-4837-8e8c-20bad8d846ba)

- Select `Web application` and give it a name

![image](https://github.com/danny-avila/LibreChat/assets/138638445/badde864-f6b5-468f-a72f-bac93326ffa5)

- Configure the `Authorized JavaScript origins`, you can add both your domain and localhost if you desire
    - Example for localhost: `http://localhost:3080`
    - Example for a domain: `https://example.com`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/f7e3763a-5f74-4850-8638-44f81693b9ac)

- Add a valid `Authorized redirect URIs`
    - Example for localhost: `http://localhost:3080/oauth/google/callback`
    - Example for a domain: `https://example.com/oauth/google/callback`
    - If using the Admin Panel: `https://example.com/api/admin/oauth/google/callback`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/0db34b19-d780-4651-9c2f-d33e24a74d55)

## .env Configuration

- Click `CREATE` and copy your `Client ID` and `Client secret`

![image](https://github.com/danny-avila/LibreChat/assets/138638445/fa8572bf-f482-457a-a285-aec7d41af76b)

- Add them to your `.env` file:

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
GOOGLE_CALLBACK_URL=/oauth/google/callback
```

- Save the `.env` file

> Note: If using docker, run `docker compose up -d` to apply the .env configuration changes

## Admin Panel Sessions

The [Admin Panel](/docs/features/admin_panel) uses the same `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`, but its callback is `${DOMAIN_SERVER}/api/admin/oauth/google/callback`. Its Google flow requests offline access and consent so an expired LibreChat admin token can be refreshed. LibreChat rechecks the user's identity, ban state, allowed domain, tenant, and current admin access on every refresh.


# Auth0 (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/auth0)

This guide walks you through configuring Auth0 as an OpenID Connect provider for LibreChat.

## Overview

Auth0 can be used as an OpenID Connect provider for LibreChat. When using Auth0 with token reuse enabled (`OPENID_REUSE_TOKENS=true`), you must configure the `OPENID_AUDIENCE` environment variable to prevent authentication issues.

## Prerequisites

- An Auth0 account with an active tenant
- Admin access to create applications and APIs in Auth0
- LibreChat instance ready for configuration

## Configuration Steps

### Step 1: Create an Auth0 Application

1. Go to **Auth0 Dashboard** → **Applications** → **Applications**
2. Click **"Create Application"**
3. Configure the application:
   - **Name**: `LibreChat` (or your preferred name)
   - **Application Type**: Select **"Single Page Application"**
4. Click **"Create"**

### Step 2: Configure Application Settings

<Callout type="warning" title="HTTPS Required">
Auth0 does not allow `http://localhost` URLs in production applications. For local development/testing, you'll need to use HTTPS. You can use services like:
- **ngrok**: `ngrok http 3080` (provides HTTPS tunnel to localhost)
- **Caddy**: Local HTTPS proxy server
- **localtunnel**: Similar to ngrok

Example with ngrok:

```bash
ngrok http 3080
# This will give you a URL like: https://abc123.ngrok.io
```

</Callout>

1. In your application's **Settings** tab:
2. Set **Allowed Callback URLs**:
   ```bash
   https://your-domain.ngrok.io/oauth/openid/callback
   ```
   (Use your ngrok URL for testing, or your production HTTPS URL)
3. Set **Allowed Logout URLs** (if using end session):
   ```bash
   https://your-domain.ngrok.io
   ```
4. Set **Allowed Web Origins**:
   ```bash
   https://your-domain.ngrok.io
   ```
5. Save the changes

### Step 3: Create an Auth0 API (Required for Token Reuse)

<Callout type="warning" title="Important for Token Reuse">
  This step is **required** when using `OPENID_REUSE_TOKENS=true`. Without it, Auth0 will return
  opaque tokens that cannot be validated by LibreChat, causing infinite refresh loops.
</Callout>

1. **Go to Auth0 Dashboard** → **Applications** → **APIs**
2. **Click "Create API"**
3. **Configure the API:**
   - **Name**: `LibreChat API` (or your preferred name)
   - **Identifier**: `https://api.librechat.ai` (or your preferred identifier)
     - **Note**: This is just a unique identifier, not an actual URL. It doesn't need to be accessible.
     - Common patterns: `https://api.yourdomain.com`, `https://librechat.yourdomain.com`, etc.
   - **Signing Algorithm**: RS256 (recommended)
4. **Click "Create"**

### Step 4: Configure Offline Access

1. **Go to your API's Settings** → **Access Settings**
2. Enable **"Allow Offline Access"**
3. Save the changes

### Step 5: Gather Configuration Values

In your Auth0 Application's **Basic Information** section, you'll find:

- **Domain**: Shows as `dev-example.us.auth0.com` (you'll need to add `https://` prefix)
- **Client ID**: A long alphanumeric string
- **Client Secret**: Hidden by default (click to reveal)

<Callout type="info" title="Important">
The **Domain** shown in Auth0 doesn't include the `https://` prefix. You must add it when configuring the `OPENID_ISSUER`.

Example: If Auth0 shows `dev-abc123.us.auth0.com`, use `https://dev-abc123.us.auth0.com`

</Callout>

### Step 6: Configure LibreChat Environment Variables

Add the following environment variables to your `.env` file:

```bash
# OpenID Connect Configuration
# Domain from Basic Information (add https:// prefix)
OPENID_ISSUER=https://dev-abc123.us.auth0.com

# Client ID from Basic Information
OPENID_CLIENT_ID=your_long_alphanumeric_client_id

# Client Secret from Basic Information (click to reveal)
OPENID_CLIENT_SECRET=your_client_secret_from_basic_information

# Callback URL (must match what's configured in Auth0)
OPENID_CALLBACK_URL=/oauth/openid/callback

# Token Configuration
OPENID_REUSE_TOKENS=true
OPENID_SCOPE=openid profile email offline_access

# IMPORTANT: Your Auth0 API identifier (from Step 3)
OPENID_AUDIENCE=https://api.librechat.ai

# Security Settings (recommended)
OPENID_USE_PKCE=true

# Session Configuration (generate a secure random string)
OPENID_SESSION_SECRET=your-secure-session-secret-32-chars-or-more

# Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
# OPENID_MAX_LOGOUT_URL_LENGTH=2000

# Optional: Custom button appearance
OPENID_BUTTON_LABEL=Continue with Auth0
# OPENID_IMAGE_URL=https://path-to-auth0-logo.png

# If using ngrok for testing, also update:
# DOMAIN_CLIENT=https://your-domain.ngrok.io
# DOMAIN_SERVER=https://your-domain.ngrok.io
```

## Understanding OPENID_AUDIENCE

### The Problem

When using Auth0 with `OPENID_REUSE_TOKENS=true`:

- Auth0 returns **opaque access tokens** (JWE format) by default
- LibreChat expects **signed JWT tokens** (JWS format) that can be validated
- Without proper configuration, this mismatch causes authentication failures and infinite refresh loops

### The Solution

The `OPENID_AUDIENCE` environment variable:

- Must be set to your Auth0 API identifier (created in Step 3)
- Forces Auth0 to issue signed JWT access tokens instead of opaque tokens
- Enables LibreChat to validate tokens using Auth0's JWKS endpoint
- May contain comma-separated audiences for JWT validation; Auth0 authorization requests use the first non-empty value

### How It Works

When `OPENID_AUDIENCE` is configured:

1. LibreChat includes the `audience` parameter in authorization requests, using the first non-empty value when multiple comma-separated audiences are configured
2. Auth0 recognizes the audience as a registered API
3. Auth0 issues JWT access tokens that can be validated
4. LibreChat successfully validates tokens and authentication works properly

## Environment Variable Reference

<OptionTable
  options={[
    [
      'OPENID_AUDIENCE',
      'string',
      'The identifier of your Auth0 API. Required when using OPENID_REUSE_TOKENS=true with Auth0 to prevent opaque token issues. Comma-separated values are accepted for JWT validation; authorization requests use the first non-empty value.',
      'OPENID_AUDIENCE=https://api.librechat.ai',
    ],
  ]}
/>

## Troubleshooting

### Infinite Refresh Loop

**Symptoms**: Page reloads continuously after clicking "Continue with OpenID"

**Solution**:

1. Ensure `OPENID_AUDIENCE` is set to your Auth0 API identifier
2. Verify the API was created in Auth0 and offline access is enabled
3. Check that the audience value matches exactly

### Invalid Token Errors

**Symptoms**: Authentication fails with token validation errors

**Solution**:

1. Enable debug logging: `DEBUG_OPENID_REQUESTS=true`
2. Verify Auth0 is returning JWT tokens (not opaque tokens)
3. Check JWKS endpoint is accessible

### Missing Refresh Token

**Symptoms**: No refresh token in authentication response

**Solution**:

1. Ensure `offline_access` is included in `OPENID_SCOPE`
2. Verify "Allow Offline Access" is enabled in your Auth0 API settings

## Best Practices

1. **Always use HTTPS** - Auth0 requires HTTPS for all callback URLs
2. **Testing locally** - Use ngrok or similar services to create HTTPS tunnels to localhost
3. **Secure your session secret** - Use a strong, random value for `OPENID_SESSION_SECRET`
4. **Enable PKCE** - Set `OPENID_USE_PKCE=true` for enhanced security
5. **Restrict callback URLs** - Only allow your actual domain in Auth0 settings
6. **Monitor logs** - Use `DEBUG_OPENID_REQUESTS=true` during setup
7. **API Identifier** - Remember it's just an identifier, not an actual endpoint that needs to exist

## Additional Resources

- [Auth0 Documentation](https://auth0.com/docs)
- [Auth0 Access Tokens](https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens)
- [LibreChat OpenID Token Reuse](/docs/configuration/authentication/OAuth2-OIDC/token-reuse)


# Authelia (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/authelia)

- Generate a client secret using:
  ```
  docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
  ```
- Then in your `configuration.yml` add the following in the oidc section:
  ```bash filename="configuration.yml"
    - client_id: 'librechat'
      client_name: 'LibreChat'
      client_secret: '$pbkdf2-GENERATED_SECRET_KEY_HERE'
      public: false
      authorization_policy: 'two_factor'
      redirect_uris:
        - 'https://LIBRECHAT.URL/oauth/openid/callback'
      scopes:
        - 'openid'
        - 'profile'
        - 'email'
      userinfo_signing_algorithm: 'none'
  ```
- Then restart Authelia

# LibreChat

- Open the `.env` file in your project folder and add the following variables:
  ```bash filename=".env"
  ALLOW_SOCIAL_LOGIN=true
  OPENID_BUTTON_LABEL='Log in with Authelia'
  OPENID_ISSUER=https://auth.example.com/.well-known/openid-configuration
  OPENID_CLIENT_ID=librechat
  OPENID_CLIENT_SECRET=ACTUAL_GENERATED_SECRET_HERE
  OPENID_SESSION_SECRET=ANY_RANDOM_STRING
  OPENID_CALLBACK_URL=/oauth/openid/callback
  OPENID_SCOPE="openid profile email"
  OPENID_IMAGE_URL=https://www.authelia.com/images/branding/logo-cropped.png
  # Optional: redirects the user to the end session endpoint after logging out
  OPENID_USE_END_SESSION_ENDPOINT=true

  # Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
  # OPENID_MAX_LOGOUT_URL_LENGTH=2000
  ```


# Authentik (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/authentik)

1. **Access Authentik Admin Interface:**

- Open the Authentik Admin Interface in your browser. Can be found at a URL such as: `https://authentik.example.com/if/admin/#/administration/overview`.
  > We will use `https://authentik.example.com` as an example URL. Replace this with the URL of your Authentik instance.

2. **Create a new Application and Provider using the wizard:**

- Click on the Applications tab in the left sidebar and click on Applications again.
- At the top of the page you should see a button that says `Create with Wizard`. Click on it.
  > Note: You can also create an application and provider manually just be sure to link them afterwards.
- You can name the application whatever you want. For this example, we will name it `LibreChat` and click next.
- Choose the `OAuth2/OIDC` provider and click next.
- Choose your authentication and authorization flows.
- Scroll down and take note of the `Client ID` and `Client Secret`. You will need these later.
- Under Advanced protocol settings change Subject mode to `Based on the User's Email`.
- Click Submit.
- Add the new application you created to an Outpost.
  > Note: You should also apply any policies for access control that you want to apply to LibreChat at this point.

3. **Gather Information for .env:**

- You will need the following information from Authentik:
  - `Client ID`
  - `Client Secret`
  - `OpenID Configuration URL`
    > All of these can be found by clicking on the provider you just created.

3. **Configure LibreChat:**

- Open the `.env` file and add the following variables:

```bash filename=".env"
OPENID_ISSUER=https://authentik.example.com/application/o/librechat/.well-known/openid-configuration
OPENID_CLIENT_ID=[YourClientID]
OPENID_CLIENT_SECRET=[YourClientSecret]
OPENID_SESSION_SECRET=[JustGenerateARandomSessionSecret]
OPENID_CALLBACK_URL=/oauth/openid/callback
OPENID_SCOPE=openid profile email
# Optional customization below
OPENID_BUTTON_LABEL=Login with Authentik
OPENID_IMAGE_URL=https://cdn.jsdelivr.net/gh/selfhst/icons/png/authentik.png
# Generate nonce for federated identity providers that require it, i.e. Cognito configured with Entra as an OIDC provider.
OPENID_GENERATE_NONCE=true
# Redirects the user to the end session endpoint after logging out
OPENID_USE_END_SESSION_ENDPOINT=true

# Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
# OPENID_MAX_LOGOUT_URL_LENGTH=2000
```

> Note: Make sure nothing is wrapped in quotes in your .env and you have allowed social login.

4. **Check Configuration:**

- Restart LibreChat to apply the changes.
- Open an Icognito window and navigate to your LibreChat instance.
- Underneath the form login there should be a new button that says `Login with Authentik`.
- You should be redirected to Authentik to login.
- After logging in you should be redirected back to LibreChat and be logged in.
  - If you are not redirected back to LibreChat, check Authentik logs for any errors.


# AWS Cognito (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/aws)

## Create a new User Pool in Cognito

- Visit: **[https://console.aws.amazon.com/cognito/](https://console.aws.amazon.com/cognito/)**
- Sign in as Root User
- Click on `Create user pool`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/e9b412c3-2cf1-4f54-998c-d1d6c12581a5)

## Configure sign-in experience

Your Cognito user pool sign-in options should include `User Name` and `Email`.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/d2cf362d-469e-4993-8466-10282da114c2)

## Configure Security Requirements

You can configure the password requirements now if you desire

![image](https://github.com/danny-avila/LibreChat/assets/32828263/e125e8f1-961b-4a38-a6b7-ed1faf29c4a3)

## Configure sign-up experience

Choose the attributes required at signup. The minimum required is `name`. If you want to require users to use their full name at sign up use: `given_name` and `family_name` as required attributes.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/558b8e2c-afbd-4dd1-87f3-c409463b5f7c)

## Configure message delivery

Send email with Cognito can be used for free for up to 50 emails a day

![image](https://github.com/danny-avila/LibreChat/assets/32828263/fcb2323b-708e-488c-9420-7eb482974648)

## Integrate your app

Select `Use Cognitio Hosted UI` and chose a domain name

![image](https://github.com/danny-avila/LibreChat/assets/32828263/111b3dd4-3b20-4e3e-80e1-7167d2ad0f62)

Set the app type to `Confidential client`
Make sure `Generate a client secret` is set.
Set the `Allowed callback URLs` to `https://YOUR_DOMAIN/oauth/openid/callback`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/1f92a532-7c4d-4632-a55d-9d00bf77fc4d)

Under `Advanced app client settings` make sure `Profile` is included in the `OpenID Connect scopes` (in the bottom)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/5b035eae-4a8e-482c-abd5-29cee6502eeb)

## Review and create

You can now make last minute changes, click on `Create user pool` when you're done reviewing the configuration

![image](https://github.com/danny-avila/LibreChat/assets/32828263/dc8b2374-9adb-4065-85dc-a087d625372d)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/67efb1e9-dfe3-4ebd-9ebb-92186c514b5c)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/9f819175-ace1-44b1-ba68-af21ac9f6735)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/3e7b8b17-4e12-49af-99cf-78981d6331df)

## Get your environment variables

1. Open your User Pool

![image](https://github.com/danny-avila/LibreChat/assets/32828263/b658ff2a-d252-4f3d-90a7-9fbde42c01db)

2. The `User Pool ID` and your AWS region will be used to construct the `OPENID_ISSUER` (see below)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/dc8ae403-cbff-4aae-9eee-42d7cf3485e7)
![image](https://github.com/danny-avila/LibreChat/assets/32828263/d606f5c8-c60b-4d20-bdb2-d0d69e49ea1e)

3. Go to the `App Integrations` tab

![image](https://github.com/danny-avila/LibreChat/assets/32828263/58713bdc-24bc-47de-bdca-020dc321e997)

4. Open the app client

![image](https://github.com/danny-avila/LibreChat/assets/32828263/271bf7d2-3df2-43a7-87fc-e50294e49b2e)

5. Toggle `Show Client Secret`

![image](https://github.com/danny-avila/LibreChat/assets/32828263/a844fe65-313d-4754-81b4-380336e0e336)

- Use the `Client ID` for `OPENID_CLIENT_ID`

- Use the `Client secret` for `OPENID_CLIENT_SECRET`

- Generate a random string for the `OPENID_SESSION_SECRET`

> The `OPENID_SCOPE` and `OPENID_CALLBACK_URL` are pre-configured with the correct values

6. Open the `.env` file at the root of your LibreChat folder and add the following variables with the values you copied:

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

OPENID_CLIENT_ID=Your client ID
OPENID_CLIENT_SECRET=Your client secret
OPENID_ISSUER=https://cognito-idp.[AWS REGION].amazonaws.com/[USER POOL ID]/.well-known/openid-configuration
OPENID_SESSION_SECRET=Any random string
OPENID_SCOPE=openid profile email
OPENID_CALLBACK_URL=/oauth/openid/callback

# Optional: redirects the user to the end session endpoint after logging out
OPENID_USE_END_SESSION_ENDPOINT=true
# Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
# OPENID_MAX_LOGOUT_URL_LENGTH=2000
# Optional: generates the nonce url parameter.
OPENID_GENERATE_NONCE=true
```

> [!WARNING]  
> If Cognito is configured with an OIDC provider, i.e. federation to Entra, the `OPENID_GENERATE_NONCE=true` is required. Otherwise Cognito will generate it regardless and the claims validation will fail since the client didn't provide one.

7. Save the .env file

> Note: If using docker, run `docker compose up -d` to apply the .env configuration changes


# Azure Entra (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/azure)

1. Go to the [Azure Portal](https://portal.azure.com/) and sign in with your account.
2. In the search box, type "Azure Entra" and click on it.
3. On the left menu, click on App registrations and then on New registration.
4. Give your app a name and select Web as the platform type.
5. In the Redirect URI field, enter your LibreChat OpenID callback URL and click on Register. For local Docker installs, use `http://localhost:3080/oauth/openid/callback`. For deployed instances, replace `http://localhost:3080` with your public `DOMAIN_SERVER` value, for example `https://chat.example.com/oauth/openid/callback`.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/2b1aabce-850e-4165-bf76-3c1984f10b6c)

6. You will see an Overview page with some information about your app. Copy the Application (client) ID and the 
Directory (tenant) ID and save them somewhere.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/e67d5e97-e26d-48a5-aa6e-50de4450b1fd)

7. On the left menu, click on Authentication and check the boxes for Access tokens and ID tokens under Implicit 
grant and hybrid flows.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/88a16cbc-ff68-4b3a-ba7b-b380cc3d2366)

8. On the left menu, click on Certificates & Secrets and then on New client secret. Give your secret a 
name and an expiration date and click on Add. You will see a Value column with your secret. Copy it and 
save it somewhere. Don't share it with anyone!

![image](https://github.com/danny-avila/LibreChat/assets/6623884/31aa6cee-5402-4ce0-a950-1b7e147aafc8)

9. If you want to restrict access by groups you should add the groups claim to the token. To do this, go to
Token configuration and click on Add group claim. Select the groups you want to include in the token and click on Add.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/c9d353f5-2cb2-4f00-b4f0-493cfec8fe9a)

10. Open the .env file in your project folder and add the following variables with the values you copied:

```bash filename=".env"
DOMAIN_CLIENT=https://your-domain.com # use http://localhost:3080 if not using a custom domain
DOMAIN_SERVER=https://your-domain.com # use http://localhost:3080 if not using a custom domain

# enable social login or else OpenID button will not appear on login page
ALLOW_SOCIAL_LOGIN=true

OPENID_CLIENT_ID=Your Application (client) ID
OPENID_CLIENT_SECRET=Your client secret
OPENID_ISSUER=https://login.microsoftonline.com/Your Directory (tenant ID)/v2.0/
OPENID_SESSION_SECRET=Any random string
OPENID_SCOPE=openid profile email #DO NOT CHANGE THIS
OPENID_CALLBACK_URL=/oauth/openid/callback # this should be the same for everyone

OPENID_REQUIRED_ROLE_TOKEN_KIND=id

# If you want to restrict access by groups
OPENID_REQUIRED_ROLE_PARAMETER_PATH="roles"
OPENID_REQUIRED_ROLE="Your Group Name" # Single role or comma-separated roles (e.g., Group1,Group2,Admin)

# Optional: redirects the user to the end session endpoint after logging out
OPENID_USE_END_SESSION_ENDPOINT=true

# Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
# OPENID_MAX_LOGOUT_URL_LENGTH=2000
```

The redirect URI registered in Azure must exactly match the URL LibreChat serves. If `DOMAIN_SERVER=https://chat.example.com`, Azure should use `https://chat.example.com/oauth/openid/callback`.

11. Save the .env file

> Note: If using docker, run `docker compose up -d` to apply the .env configuration changes

## Advanced: Token Reuse

LibreChat supports reusing Azure Entra ID tokens for session management, which can provide better integration with your Azure environment. This feature allows LibreChat to use Azure's refresh tokens instead of managing its own session tokens.

To learn more about this feature and how to configure it, see [Re-use OpenID Tokens for Login Session](/docs/configuration/authentication/OAuth2-OIDC/token-reuse).

## Advanced: Microsoft Graph API Integration

When using Azure Entra ID as your OpenID provider, you can enable Microsoft Graph API integration to enhance the permissions and sharing system with people and group search capabilities.

### Prerequisites

1. Your Azure app registration must have the appropriate Microsoft Graph API permissions
2. Admin consent may be required for certain Graph API scopes (like `GroupMember.Read.All`)

### Adding Graph API Permissions

1. In your Azure app registration, go to **API permissions**
2. Click **Add a permission** > **Microsoft Graph** > **Delegated permissions**
3. Add these permissions:
   - `User.Read` - Sign in and read user profile
   - `People.Read` - Read user contacts
   - `GroupMember.Read.All` - Read all group memberships
   - `User.ReadBasic.All` - Read all users' basic profiles
4. Click **Grant admin consent** if required (you'll need admin privileges)

### Configuration

<Callout type="error" title="Required: Enable Token Reuse">
**Important:** You MUST enable OpenID token reuse for this feature to work:
```bash filename=".env"
OPENID_REUSE_TOKENS=true
```
See [Token Reuse Configuration](#advanced-token-reuse) above for details.
</Callout>

Add the following environment variables to your `.env` file:

```bash filename=".env"
# Enable Entra ID people search in permissions/sharing
USE_ENTRA_ID_FOR_PEOPLE_SEARCH=true

# Include group owners as members when searching groups
ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS=true

# Microsoft Graph API scopes (these are automatically included with the OpenID scopes)
OPENID_GRAPH_SCOPES=User.Read,People.Read,GroupMember.Read.All,User.ReadBasic.All
```

When enabled, the people picker in the permissions and sharing dialogs will:
- Search both local LibreChat users and Azure Entra ID users
- Display user profiles with names and emails from your organization
- Allow searching and selecting Azure Entra ID groups
- Show group members based on your Graph API permissions

### Notes

- **Token reuse (`OPENID_REUSE_TOKENS=true`) is mandatory** for this feature to work
- The `OPENID_GRAPH_SCOPES` are automatically appended to your existing `OPENID_SCOPE` during authentication
- Group search requires the `GroupMember.Read.All` permission, which typically needs admin consent
- User search works with basic `User.Read`, `People.Read`, and `User.ReadBasic.All` permissions

## Advanced: SharePoint Integration

LibreChat can integrate with SharePoint Online and OneDrive for Business, allowing users to browse and attach files directly from their SharePoint libraries.

### Prerequisites

1. All requirements from [Token Reuse](#advanced-token-reuse) must be met
2. Your Azure app registration needs additional SharePoint permissions
3. Your Azure app registration must expose and grant a LibreChat API scope, such as `api://<client-id>/access_as_user`

### Adding SharePoint Permissions

1. In your Azure app registration, go to **API permissions**
2. Click **Add a permission**

#### For SharePoint Access:
3. Select **SharePoint** (not Microsoft Graph)
4. Choose **Delegated permissions**
5. Add: `AllSites.Read` - Read items in all site collections

#### For File Downloads:
6. Click **Add a permission** again
7. Select **Microsoft Graph**
8. Choose **Delegated permissions**
9. Add: `Files.Read.All` - Read all files that user can access

10. Click **Grant admin consent** for both permissions

### Configuration

Before enabling the SharePoint variables, make sure the OpenID token reuse configuration requests the LibreChat app API scope. This gives Azure an app-audience access token that can be used as the on-behalf-of assertion for SharePoint and Graph token exchange.

```bash filename=".env"
# OpenID token reuse and OBO-compatible audience
OPENID_REUSE_TOKENS=true
OPENID_SCOPE=openid profile email offline_access api://<client-id>/access_as_user
OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true

# Enable SharePoint file picker
ENABLE_SHAREPOINT_FILEPICKER=true

# Your SharePoint tenant URL
SHAREPOINT_BASE_URL=https://yourtenant.sharepoint.com

# SharePoint scope for file picker (replace 'yourtenant' with your actual tenant)
SHAREPOINT_PICKER_SHAREPOINT_SCOPE=https://yourtenant.sharepoint.com/AllSites.Read

# Graph API scope for downloading files
SHAREPOINT_PICKER_GRAPH_SCOPE=Files.Read.All
```

### Usage

When properly configured:
1. Users will see "From SharePoint" option in the file attachment menu
2. Clicking it opens the native SharePoint file picker
3. Users can browse and select files from any SharePoint site or OneDrive they have access to
4. Selected files are downloaded and attached to the conversation

<Callout type="warning" title="Security Note">
The SharePoint integration respects all existing SharePoint permissions. Users can only access files they already have permission to view in SharePoint/OneDrive.
</Callout>

For detailed troubleshooting and advanced configuration, see: [SharePoint Integration Guide](/docs/configuration/sharepoint)


# Keycloak (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/keycloak)

1. **Access Keycloak Admin Console:**
- Open the Keycloak Admin Console in your web browser. This is usually 
found at a URL like `http://localhost:8080/auth/admin/`.

2. **Create a Realm (if necessary):**
- If you don't already have a realm for your application, create one. Click on 'Add Realm' and give it a name.

3. **Create a Client:**
- Within your realm, click on 'Clients' and then 'Create'.
- Enter a client ID and select 'openid-connect' as the Client Protocol.
- Set 'Client Authentication' to 'On'.
- In 'Valid Redirect URIs', enter `http://localhost:3080/oauth/openid/callback` or the appropriate URI for 
your application.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/d956de3d-e1f7-4327-818a-f146eb86a949)

![image](https://github.com/danny-avila/LibreChat/assets/6623884/fbefbc05-b4ec-4122-8229-54a0a5876d76)

![image](https://github.com/danny-avila/LibreChat/assets/6623884/f75c7b0f-030e-4182-bf87-ccf3aeae17d4)


4. **Configure Client:**
- After creating the client, you will be redirected to its settings page.
- Note the 'Client ID' and 'Secret' from the 'Credentials' tab – you'll need these for your application.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/b1c1f0b6-641b-4cf7-a7f1-a9a32026d51b)


5. **Add Roles (Optional):**
If you want to restrict access to users with specific roles, you can define roles in Keycloak and assign them to users.
- Go to the 'Roles' tab in your client or realm (depending on where you want to define the roles).
- Create roles that match the value(s) you have in `OPENID_REQUIRED_ROLE`.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/67ca635f-5082-4dcc-97ac-019029a81d7c)

6. **Assign Roles to Users (Optional):**
- Go to 'Users', select a user, and go to the 'Role Mappings' tab.
- Assign at least one of the roles specified in `OPENID_REQUIRED_ROLE` to the user.

![image](https://github.com/danny-avila/LibreChat/assets/6623884/f2ea70ed-e16c-4ec8-b84f-79fbfca627be)

7. **Get path of roles list inside token (Optional):**
- Decode your jwtToken from OpenID provider and determine path for roles list inside access token. For example, if you are 
    using Keycloak, the path is `realm_access.roles`.
- Put this path in `OPENID_REQUIRED_ROLE_PARAMETER_PATH` variable in `.env` file.
- By parameter `OPENID_REQUIRED_ROLE_TOKEN_KIND` you can specify which token kind you want to use. 
 Possible values are `access` and `id`.

8. **Update Your Project's Configuration:**
- Open the `.env` file in your project folder and add the following variables:
  ```bash filename=".env"
  OPENID_ISSUER=http://localhost:8080/realms/[YourRealmName]
  OPENID_CLIENT_ID=[YourClientID]
  OPENID_CLIENT_SECRET=[YourClientSecret]
  OPENID_SESSION_SECRET=[JustGenerateARandomSessionSecret]
  OPENID_CALLBACK_URL=/oauth/openid/callback
  OPENID_SCOPE="openid profile email"
  OPENID_REQUIRED_ROLE=[YourRequiredRole] # Single role or comma-separated roles (e.g., role1,role2,admin)
  OPENID_REQUIRED_ROLE_TOKEN_KIND=(access|id) # that means, `access` or `id`
  OPENID_REQUIRED_ROLE_PARAMETER_PATH="realm_access.roles"

  # Optional: redirects the user to the end session endpoint after logging out
  OPENID_USE_END_SESSION_ENDPOINT=true

  # Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
  # OPENID_MAX_LOGOUT_URL_LENGTH=2000
  ```


# OpenID Connect Token Reuse (https://www.librechat.ai/docs/configuration/authentication/OAuth2-OIDC/token-reuse)

LibreChat supports reusing access and refresh tokens issued by your OpenID Connect provider (like Azure Entra ID or Auth0) to manage user authentication state. When this feature is active, the refresh token passed to the user as a cookie is issued by your OpenID provider instead of LibreChat, allowing LibreChat servers to refresh it and request access tokens from your provider.

## Prerequisites

- A configured OpenID Connect provider (like Azure Entra ID, Auth0, etc.)
- Basic OpenID Connect setup completed

## Configuration Steps

1. Set `OPENID_REUSE_TOKENS=true` in your environment variables.

## Provider-Specific Configuration

### Auth0 Configuration

<Callout type="warning" title="Important for Auth0">
  When using Auth0 with token reuse, you **must** configure the `OPENID_AUDIENCE` environment
  variable. Without it, Auth0 will return opaque tokens that cannot be validated by LibreChat,
  causing infinite refresh loops.
</Callout>

For Auth0, you need to:

1. Create an API in Auth0 (required for JWT access tokens):
   - Go to **Auth0 Dashboard** → **Applications** → **APIs**
   - Click **"Create API"**
   - Set an **Identifier** (e.g., `https://api.librechat.com`)
   - Enable **"Allow Offline Access"** in the API settings

2. Set the required environment variables:
   ```bash filename=".env"
   # Required for Auth0
   OPENID_AUDIENCE=https://api.librechat.com  # Your API identifier from Auth0
   OPENID_SCOPE=openid profile email offline_access
   ```

For detailed Auth0 configuration, see: [Auth0 OpenID Connect Configuration](/docs/configuration/authentication/OAuth2-OIDC/auth0)

### Azure Entra ID Configuration

2. Configure your OpenID provider (using Azure Entra ID as an example):
   - Go to the Azure Portal and navigate to your app registration
   - Click on "Expose API" in the left menu
   - Click "Add" next to "Application ID URI"
   - Enter your API URI (e.g., "api://librechat") and save

![image](https://github.com/user-attachments/assets/95bcd3ef-ed54-4002-9c74-a98d15673e71)

3. Create an API scope:
   - In the "Expose API" section, click "Add a scope"
   - Configure the scope with appropriate permissions
   - Save the scope configuration

![image](https://github.com/user-attachments/assets/6f7a7170-92cd-4816-a1a8-dcacb592c893)

4. Configure API permissions:
   - Go to "API permissions" in the left menu
   - Click "Add a permission"
   - Under "APIs my organization uses", search for your app
   - Select "Delegated permissions" and choose the appropriate scope (e.g., "access_user")

![image](https://github.com/user-attachments/assets/d0b23d65-ae39-4eb8-9f4d-97f18c627045)

![image](https://github.com/user-attachments/assets/67d09375-5b80-45e2-89e6-3bd0a00288e4)

![image](https://github.com/user-attachments/assets/1f85ae5e-4be2-4a68-b663-4152f32fa108)

5. Set the required scope in your environment:

   ```bash filename=".env"
   OPENID_SCOPE=api://librechat/.default openid profile email offline_access
   ```

   Note: The `offline_access` scope is required to obtain a refresh token for reuse.

6. Grant admin consent:
   - Go to Enterprise Applications in Azure Portal
   - Find your LibreChat application
   - Navigate to Security > Permissions
   - Click "Grant admin consent"

![image](https://github.com/user-attachments/assets/d3a8b1f3-0f0a-4a90-92d9-9df91e665c8d)

7. Accept the requested permissions in the popup

![image](https://github.com/user-attachments/assets/cc3eb15a-994e-49fb-b212-f94260d787d3)

8. Clear LibreChat cache and restart the service.

<Callout type="info" title="Microsoft Graph API Integration">
  When using Azure Entra ID with token reuse, you can also enable Microsoft Graph API integration
  for enhanced people and group search capabilities. See [Microsoft Graph API
  Integration](/docs/configuration/authentication/OAuth2-OIDC/azure#advanced-microsoft-graph-api-integration)
  for more details.
</Callout>

## Environment Variables

```bash filename=".env"
# OpenID Token Reuse Configuration
OPENID_REUSE_TOKENS=true
OPENID_SCOPE=api://librechat/.default openid profile email offline_access
OPENID_REUSE_MAX_SESSION_AGE_MS=900000  # 15 minutes in milliseconds
# OPENID_REFRESH_BRIDGE_GRACE_MS=60000  # 1-minute rotation recovery window

# Required for Auth0 (use your API identifier)
# OPENID_AUDIENCE=https://api.librechat.com

# Caching Configuration
OPENID_JWKS_URL_CACHE_ENABLED=true
OPENID_JWKS_URL_CACHE_TIME=600000  # 10 minutes in milliseconds

# Azure-specific Configuration
OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true
OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE=user.read
# Optional scope for Microsoft Graph tokens inserted into MCP configuration
# GRAPH_API_SCOPES=https://graph.microsoft.com/.default

# Logout Configuration
OPENID_USE_END_SESSION_ENDPOINT=true

# Maximum logout URL length before using logout_hint instead of id_token_hint (default: 2000)
# OPENID_MAX_LOGOUT_URL_LENGTH=2000
```

## Additional Configuration Options

- `OPENID_AUDIENCE`: Audience value for JWT validation and authorization requests. **Required for Auth0** to receive JWT access tokens instead of opaque tokens. Comma-separated values are accepted for JWT validation; authorization requests use the first non-empty value.
- `OPENID_REUSE_MAX_SESSION_AGE_MS`: Maximum age a reused OpenID session token is served before LibreChat forces an IdP refresh (default: 900000 ms / 15 minutes). Accepts arithmetic expressions like `60 * 60 * 24 * 1000`. Increase it toward your IdP access-token lifetime if your provider revokes the previous access token whenever it refreshes.
- `OPENID_REFRESH_BRIDGE_GRACE_MS`: Short recovery window for a rotated refresh token while LibreChat publishes the refreshed session (default: 60000 ms / 1 minute). It accepts arithmetic expressions. Increase it only when slow session persistence or cross-replica publication needs more time.
- `OPENID_JWKS_URL_CACHE_ENABLED`: Enables caching of signing key verification results to prevent excessive HTTP requests to the JWKS endpoint
- `OPENID_JWKS_URL_CACHE_TIME`: Cache duration in milliseconds (default: 600000 ms / 10 minutes)
- `OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED`: Enables on-behalf-of flow for user info (Azure-specific)
- `OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE`: Scope for user info in on-behalf-of flow (Azure-specific)
- `GRAPH_API_SCOPES`: Space-separated Microsoft Graph scopes requested when resolving `{{LIBRECHAT_GRAPH_ACCESS_TOKEN}}` in a YAML-defined MCP server. Defaults to `https://graph.microsoft.com/.default`; this is separate from the `OPENID_GRAPH_SCOPES` used for people and group search.
- `OPENID_USE_END_SESSION_ENDPOINT`: Enables use of the end session endpoint for logout
- `OPENID_MAX_LOGOUT_URL_LENGTH`: Maximum URL length before using `logout_hint` instead of `id_token_hint` to prevent URI too long errors (default: 2000)

## Security Considerations

- Ensure proper token storage and handling
- Implement appropriate token refresh mechanisms
- Monitor token usage and implement rate limiting if necessary
- Regularly rotate client secrets
- Use secure cookie settings for token storage
- LibreChat rejects ID tokens when an access token is required for bearer-token reuse; configure the provider to issue a real access token for the requested audience and scopes
- Token responses with missing, invalid, or excessive `expires_in` values are bounded to the credential's safe lifetime instead of extending a cached token beyond it
- LibreChat resolves OpenID credentials for MCP and Microsoft Graph from the live session, coordinates refreshes across replicas, and fences logout so a late refresh cannot recreate a signed-out session

## Troubleshooting

If you encounter issues with token reuse:

1. Verify all required scopes are properly configured
2. Check that admin consent has been granted
3. Ensure the API permissions are correctly set up
4. Verify the token cache is working as expected
5. Check the application logs for any authentication errors
6. Enable detailed OpenID request header logging by setting `DEBUG_OPENID_REQUESTS=true` in your environment variables to see request headers in addition to URLs (with sensitive data masked)


# Overview (https://www.librechat.ai/docs/configuration/authentication/SAML)

## Overview

SAML (Security Assertion Markup Language) is a widely used authentication
protocol that enables Single Sign-On (SSO). It allows users to authenticate once
with an Identity Provider (IdP) and gain access to multiple services without
needing to log in again.

<Callout type="warning" title="SLO (Single Logout) Not Supported">
Single Logout (SLO) is not supported in this implementation.
</Callout>

<Callout type="warning" title="Mutual Exclusion of OpenID and SAML">
If OpenID authentication is enabled, SAML authentication will be automatically disabled.

Only one authentication method can be active at a time.
</Callout>

## Stable Identity Binding

LibreChat binds SAML accounts to the assertion's stable NameID, not to a mutable email address. A first login can match an existing SAML account by email and record its NameID, but later logins must present the same NameID. An existing account refuses a different NameID even when the email claim matches.

Request a stable format from the IdP and optionally pin its entity ID:

```env
SAML_NAME_ID_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
SAML_IDP_ISSUER=https://idp.example.com
```

`SAML_NAME_ID_FORMAT` controls the format requested in the authentication request. Persistent NameIDs are recommended. LibreChat rejects a transient format in configuration and rejects assertions that identify their NameID as transient. `SAML_IDP_ISSUER`, when set, requires the assertion issuer to match exactly; a missing or different issuer is rejected.

Email remains required for profile and domain-policy checks. The deployment's and tenant's allowed email-domain policies still apply on every login; they do not replace the stable NameID binding.

## Authentication Method Activation Based on Environment Variables

The following table indicates which authentication method is enabled depending
on the environment variable settings:

|   OIDC   |   SAML   | Active Authentication Method |
| -------- | -------- | ---------------------------- |
| ✅Enabled  | ❌Disabled | OpenID Connect (OIDC)        |
| ❌Disabled | ✅Enabled  | SAML                         |
| ✅Enabled  | ✅Enabled  | OpenID Connect (OIDC)        |
| ❌Disabled | ❌Disabled | No authentication enabled    |

## SAML Certificate Format and Configuration

The `SAML_CERT` environment variable is used to specify the Identity Provider’s (IdP) signing certificate for validating SAML Responses. This certificate must be provided in **PEM format** and can be specified in one of the following ways:

### As a File Path (Relative or Absolute)

If `SAML_CERT` is set to a file path, the application will load the certificate from the specified file.
Both **relative paths** and **absolute paths** are supported.

```env
# Relative path (resolved based on the application root)
SAML_CERT=idp-cert.pem

# Absolute path
SAML_CERT=/path/to/idp-cert.pem
```

**Example File Content (`idp-cert.pem`):**

```
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUKhXaFJGJJPx466rl...
-----END CERTIFICATE-----
```

### As a One-Line PEM String

The certificate can also be provided as a **one-line PEM string** (Base64-encoded, without line breaks).

```env
SAML_CERT="MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh...W=="
```

This format is useful when storing the certificate directly in environment variables.

### As a Multi-Line PEM String (with \n escape sequences)

The certificate can also be provided as a **multi-line PEM string**  where newlines are represented as \n.

```env
SAML_CERT="-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUKhXaFJGJJPx466rl...\n-----END CERTIFICATE-----\n"
```

This format is useful when configuring certificates in .env files while preserving the full PEM structure.

### Certificate Format Requirements
- The certificate **must always be in PEM format** (Base64-encoded X.509 certificate).
- If provided as a file, it must be a valid **RFC7468 strict textual message PEM format**.
- When using a one-line certificate, ensure there are **no line breaks** in the value.
- When using a multi-line string, ensure newlines are represented as **\n** escape sequences.

For more details, refer to the [node-saml documentation](https://github.com/node-saml/node-saml/tree/master?tab=readme-ov-file#configuration-option-idpcert).


## Display Username Determination Flow Based on SAML Attributes

![auth0-1](/images/docs/configuration/authentication/SAML/username.png)

In SAML authentication, the display username is determined according to the following flow.

```mermaid
flowchart TD
    A[Start] --> B{{Is SAML_NAME_CLAIM available?}}
    B -- Yes --> C[Value of SAML_NAME_CLAIM]
    B -- No --> D{{Are both SAML_GIVEN_NAME_CLAIM & SAML_FAMILY_NAME_CLAIM available?}}
    D -- Yes --> E[Value of SAML_GIVEN_NAME_CLAIM / SAML_FAMILY_NAME_CLAIM]
    D -- No --> F{{Is only SAML_GIVEN_NAME_CLAIM available?}}
    F -- Yes --> G[Value of SAML_GIVEN_NAME_CLAIM]
    F -- No --> H{{Is only SAML_FAMILY_NAME_CLAIM available?}}
    H -- Yes --> I[Value of SAML_FAMILY_NAME_CLAIM]
    H -- No --> J{{Is SAML_USERNAME_CLAIM available?}}
    J -- Yes --> K[Value of SAML_USERNAME_CLAIM]
    J -- No --> L[Value of SAML_EMAIL_CLAIM]
    style C fill:#FFDEA5,stroke:#FFA500
    style E fill:#FFDEA5,stroke:#FFA500
    style G fill:#FFDEA5,stroke:#FFA500
    style I fill:#FFDEA5,stroke:#FFA500
    style K fill:#FFDEA5,stroke:#FFA500
    style L fill:#FFDEA5,stroke:#FFA500
```

### Determination Rules

1. If `SAML_NAME_CLAIM` is provided, its value is used as the display username.
2. If both `SAML_GIVEN_NAME_CLAIM` and `SAML_FAMILY_NAME_CLAIM` are provided, their corresponding values are concatenated to form the username.
3. If only `SAML_GIVEN_NAME_CLAIM` is provided, its value is used.
4. If only `SAML_FAMILY_NAME_CLAIM` is provided, its value is used.
5. If `SAML_USERNAME_CLAIM` is provided, its value is used.
6. If none of the above attributes are provided, `SAML_EMAIL_CLAIM` is used as the display username.

By following this flow, an appropriate username is determined during SAML authentication.

## Configuration Examples
  - [Auth0](/docs/configuration/authentication/SAML/auth0)


# Auth0 (https://www.librechat.ai/docs/configuration/authentication/SAML/auth0)

## Step 1: Create a SAML Application in Auth0

1. Log in to your Auth0 Dashboard.
2. Navigate to `Applications > Applications`.
3. Click `Create Application`.
4. Enter an Application Name (e.g., `LibreChat`) and select `Regular Web Application`.
5. Click `Create`.

![auth0-1](/images/docs/configuration/authentication/SAML/auth0/1.png)


## Step 2: Configure the SAML Add-On

1. Open the newly created application in Auth0.
2. Go to the `Addons` tab.
3. Click the slider to enable the `SAML2 Web App`.
4. Click `SAML2 Web App` panel.
5. Configure the following settings:
   - **Application Callback URL**: Set this to your LibreChat SAML callback URL:
   `https://YOUR_DOMAIN/oauth/saml/callback`
   - **Settings (JSON Format)**: Use the following configuration:
        ```json
        {
            "audience": "https://your-librechat-domain.com",
            "mappings": {
                "email": "email",
                "name": "username"
            }
        }
        ```
        <Callout type="warning" title="Audience must match SAML_ISSUER">
        Set `audience` to the same string you will use for `SAML_ISSUER` in Step 4.
        Leave it out and Auth0 asserts its own default audience, which will not match
        what LibreChat sends, and the login fails with an audience mismatch.
        </Callout>
        <Callout type="note" title="note">
        If your application requires additional attributes such as `given_name`,
        `family_name`, `username` or `picture`, ensure these mappings are properly
        configured in the Auth0 SAML settings.
        </Callout>
6. Click `Save`.


![auth0-2](/images/docs/configuration/authentication/SAML/auth0/2.png)
![auth0-3](/images/docs/configuration/authentication/SAML/auth0/3.png)

## Step 3: Obtain the Auth0 SAML Metadata

1. Once SAML is enabled, go back to the `SAML2 Web App` settings.
2. Go to the `Usage` tab.
3. Click on `Identity Provider Certificate: Download Auth0 certificate`.
4. Use the `Identity Provider Login URL` for `SAML_ENTRY_POINT`.
5. Use the `Issuer` for `SAML_IDP_ISSUER` (optional, see the callout below).
6. Copy the downloaded cert file to your project folder.

<Callout type="warning" title="SAML_ISSUER is yours, not Auth0's">

`SAML_ISSUER` is the entity ID **LibreChat sends about itself** in its authentication requests, not a value you copy out of Auth0. You choose it, and it is the same string you enter on the Auth0 side as the **Audience**. Your LibreChat base URL is the conventional choice:

```bash filename=".env"
SAML_ISSUER=https://your-librechat-domain.com
```

Auth0's own `Issuer` value goes in the separate `SAML_IDP_ISSUER` variable, which LibreChat uses to check that an assertion really came from your identity provider.

LibreChat does not publish a SAML metadata document, so there is no metadata URL to hand to Auth0. Configure Auth0 by hand with the Audience above and the callback URL below.

</Callout>

![auth0-4](/images/docs/configuration/authentication/SAML/auth0/4a.png)

## Step 4: Configure LibreChat with SAML

Open the `.env` file in your project folder and add the following variables:

  ```bash filename=".env"
  SAML_ENTRY_POINT=https://dev-xxxxx.us.auth0.com/samlp/aaaaaa

  # Your own entity ID, sent to Auth0. Must match the Audience you set in Auth0.
  SAML_ISSUER=https://your-librechat-domain.com
  # Auth0's Issuer, used to verify incoming assertions (optional)
  SAML_IDP_ISSUER=urn:dev-xxxxx.us.auth0.com

  SAML_CERT=dev-xxxxx.pem
  SAML_CALLBACK_URL=/oauth/saml/callback
  SAML_SESSION_SECRET=[JustGenerateARandomSessionSecret]

  # Attribute mappings (optional)
  SAML_EMAIL_CLAIM=
  SAML_USERNAME_CLAIM=
  SAML_GIVEN_NAME_CLAIM=
  SAML_FAMILY_NAME_CLAIM=
  SAML_PICTURE_CLAIM=
  SAML_NAME_CLAIM=

  # Login button settings (optional)
  SAML_BUTTON_LABEL=
  SAML_IMAGE_URL=

  # Whether the SAML Response should be signed.
  # - If "true", the entire `SAML Response` will be signed.
  # - If "false" or unset, only the `SAML Assertion` will be signed (default behavior).
  # SAML_USE_AUTHN_RESPONSE_SIGNED=
  ```

# AI Setup (https://www.librechat.ai/docs/configuration/pre_configured_ai)

This section provides detailed configuration guides to help you set up various AI providers and their respective APIs and credentials in LibreChat.

## Endpoints Configuration

The term "Endpoints" refers to the AI provider, configuration, or API that you need to set up and integrate with LibreChat. Each endpoint has its own configuration process, which may involve obtaining API keys, credentials, or following specific setup instructions.

The following guides are available to help you configure different endpoints:

- **[AWS Bedrock](/docs/configuration/pre_configured_ai/bedrock)**
    - Setup AWS Bedrock integration
- **[Anthropic](/docs/configuration/pre_configured_ai/anthropic)**
    - Integrate Anthropic AI models
- **[OpenAI](/docs/configuration/pre_configured_ai/openai)**
    - Set up OpenAI API integration
- **[Google](/docs/configuration/pre_configured_ai/google)**
    - Configure Google AI services
- **[Assistants](/docs/configuration/pre_configured_ai/assistants)**
    - Enable and configure OpenAI's Assistants

## Custom Endpoint Configuration

The **[librechat.yaml Configuration Guides](/docs/configuration/librechat_yaml)** provides detailed instructions on how to configure custom endpoints within LibreChat.

In addition to the pre-configured endpoints, the librechat config file allows you to add and configure custom endpoints. This includes integrating with AI providers like Ollama, Mistral AI, Openrouter, and a multitude of other third-party services.

By following these configuration guides, you can seamlessly integrate various AI providers, unlock their capabilities, and enhance your LibreChat experience with the power of multiple AI models and services.

# Anthropic (https://www.librechat.ai/docs/configuration/pre_configured_ai/anthropic)

- Create an account at **[https://platform.claude.com/](https://platform.claude.com/)**
- Go to **[https://platform.claude.com/settings/keys](https://platform.claude.com/settings/keys)** and get your api key
- You will need to set the following environment variable to your key or you can set it to `user_provided` for users to provide their own.

```bash filename=".env"
ANTHROPIC_API_KEY=user_provided
```

- You can determine which models you would like to have available with `ANTHROPIC_MODELS`.

```bash filename=".env"
ANTHROPIC_MODELS=claude-fable-5-1,claude-fable-5,claude-opus-5,claude-opus-4-8,claude-opus-4-7,claude-sonnet-5,claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022
```

**Notes:**

- Claude Fable 5.1 and Fable 5 are included in LibreChat's default Anthropic model catalog. Fable/Mythos-class models use the modern Anthropic profile in LibreChat: 1M context, 128K max output, adaptive thinking, prompt caching, and `thinkingDisplay` support for summarized or omitted reasoning output. Fable 5.1 keeps Fable 5's $10 input and $50 output rates per million tokens, with $12.50 cache writes and $0.25 cache reads.
- Claude Opus 5 is available through direct Anthropic, Anthropic on Vertex AI, and AWS Bedrock. It has a 1M-token context window and 128K-token maximum output. Adaptive thinking is enabled unless explicitly disabled; when disabled, LibreChat limits `xhigh` or `max` effort to `high` to keep requests valid.
- Claude Sonnet 5 is available through direct Anthropic, Anthropic on Vertex AI, and AWS Bedrock. LibreChat assigns it a 1M-token context window and 128K-token maximum output, uses adaptive thinking with summarized display when thinking is enabled, and sends an explicit disabled setting when a user turns thinking off.
- Native Anthropic prompt-cache controls and 1M-context detection evaluate the configured Claude model ID directly. Compatible Sonnet and Opus IDs at version 4.6 or later, including matching later-version IDs, retain those controls without waiting for an exact catalog entry. Unsupported models keep prompt-cache controls hidden in the Agent Builder.
- Anthropic endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings) via the `librechat.yaml` configuration file, including `streamRate`, `headers`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`
- To configure Anthropic or an Anthropic-compatible gateway as a separate custom endpoint, use [`provider: "anthropic"`](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#provider).

---

## Vertex AI

LibreChat supports running Anthropic Claude models through **Google Cloud Vertex AI**. This is useful if you:

- Already have Google Cloud infrastructure
- Want to use your existing GCP billing and credentials
- Need to comply with regional data residency requirements

### Quick Setup (Environment Variables)

Set the following environment variables to enable Anthropic via Vertex AI:

```bash filename=".env"
# Enable Vertex AI mode for Anthropic
ANTHROPIC_USE_VERTEX=true

# Vertex AI region (optional, defaults to 'us-east5')
# Modern models such as Fable 5.1 and Opus 5 require global, us, or eu.
# Specific regions serve Sonnet 4.6 and earlier.
ANTHROPIC_VERTEX_REGION=global

# Path to Google service account key file (optional)
# If not specified, uses default path: api/data/auth.json
GOOGLE_SERVICE_KEY_FILE=/path/to/service-account.json
```

### Prerequisites

1. **Google Cloud Project** with Vertex AI API enabled
2. **Service Account** with the following roles:
   - `Vertex AI User` (`roles/aiplatform.user`)
   - Or `Vertex AI Administrator` for full access
3. **Claude models** enabled in your Vertex AI Model Garden
4. **Service Account Key** (JSON file) downloaded and accessible to LibreChat

### Advanced Configuration

For model name mapping and advanced settings (similar to Azure OpenAI configuration), use the `librechat.yaml` file:

```yaml filename="librechat.yaml"
endpoints:
  anthropic:
    vertex:
      region: global
      models:
        claude-fable-5-1:
          deploymentName: claude-fable-5-1
        claude-opus-5:
          deploymentName: claude-opus-5
        claude-sonnet-5:
          deploymentName: claude-sonnet-5
        claude-3.5-haiku:
          deploymentName: claude-3-5-haiku@20241022
```

For detailed YAML configuration options, see [Anthropic Vertex AI Configuration](/docs/configuration/librechat_yaml/object_structure/anthropic_vertex).


# OpenAI (https://www.librechat.ai/docs/configuration/pre_configured_ai/openai)

To get your OpenAI API key, you need to:

- Go to **[https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys)**
- Create an account or log in with your existing one
- Add a payment method to your account
- Go to https://platform.openai.com/api-keys to get a key.
- You will need to set the following environment variable to your key, or you can set it to `user_provided` for users to provide their own.

```bash filename=".env"
OPENAI_API_KEY=user_provided
```

<Callout type="info" title="Where users enter their own key">

With `user_provided`, you supply no key at all: each user enters their own from the chat UI. Open the endpoint menu, and next to **OpenAI** there is a gear icon labelled **Set API Key**. Clicking it opens a dialog with a field for the key and a dropdown for how long it should be kept: 30 minutes, 2 hours, 12 hours (the default), 1 day, 7 days, 30 days, or never expire.

The key is encrypted and stored server-side against that user's account, so it is entered once rather than per conversation, and it is never shared with other users. The same dialog has a **Revoke** action, plus **Revoke All** to clear every key that user has stored.

If the endpoint menu is hidden, for example because a model spec disables `modelSelect`, the same dialog is reachable from **Settings** under **Data controls**, in the **API keys** section.

Until a user sets a key, the endpoint is visible but unusable for them.

</Callout>

- You can determine which models you would like to have available with `OPENAI_MODELS`
  - When `OPENAI_API_KEY` is set to `user_provided` → only the models put in this list will be available
    - ⚠️New models won't automatically show up; you'll need to add them to this list first
  - When `OPENAI_API_KEY` is set to the actual API key value → as long as `OPENAI_MODELS` is left commented-out, it will do an API call to find out what models are available, which should include any new ones

```bash filename=".env"
OPENAI_MODELS=gpt-5.6,gpt-5.6-terra,gpt-5.6-luna,gpt-5.5,gpt-5.5-pro,chat-latest,gpt-5.4,gpt-5.4-pro,gpt-5.4-mini,gpt-5.4-nano,gpt-5.3-codex,gpt-5.2,gpt-5,gpt-5-codex,gpt-5-mini,gpt-5-nano,o3-pro,o3,o4-mini,gpt-4.1,gpt-4.1-mini,gpt-4.1-nano,o3-mini,o1-pro,o1,gpt-4o,gpt-4o-mini
```

**Notes:**

- Internal OpenAI defaults include `gpt-5.6`, `gpt-5.6-terra`, and `gpt-5.6-luna`, followed by the existing GPT-5 catalog. `gpt-5.6` is the Sol tier alias; LibreChat assigns all three GPT-5.6 tiers a 1,050,000-token context window and 128,000-token maximum output. Deprecated legacy defaults are no longer included unless you add them explicitly with `OPENAI_MODELS`.
- GPT-5.6 supports the Responses API-only `reasoning_mode` values `standard` and `pro`, plus `reasoning_context` values `auto`, `current_turn`, and `all_turns`. These can be configured in [model specs](/docs/configuration/librechat_yaml/object_structure/model_specs#reasoning_mode).
- For a GPT-5.6 model with a non-`none` `reasoning_effort`, LibreChat defaults the canonical OpenAI endpoint to the Responses API so function tools remain compatible. An explicit `useResponsesApi: false` keeps Chat Completions. Azure OpenAI, OpenRouter, and custom or reverse-proxy base URLs are not switched automatically.
- Selecting a vision model for messages with attachments is not necessary as it will be switched behind the scenes for you. If you didn't outright select a vision model, it will only be used for the vision request and you should still see the non-vision model you had selected after the request is successful
- OpenAI Vision models allow for messages without attachments
- OpenAI endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings) via the `librechat.yaml` configuration file, including `streamRate`, `headers`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`


# Google (https://www.librechat.ai/docs/configuration/pre_configured_ai/google)

For the Google endpoint, you can use either the **Gemini API through Google AI Studio** or the **Vertex AI API**.

The Generative Language API uses an API key, which you can get from **Google AI Studio**.

For Vertex AI, you need a Service Account JSON key file, with appropriate access configured.

Instructions for both are given below.

## Generative Language API (Gemini)

**[See here for Gemini API pricing and rate limits](https://ai.google.dev/pricing)**

⚠️ While Google models are free, they are using your input/output to help improve the model, with data de-identified from your Google Account and API key.
⚠️ During this period, your messages “may be accessible to trained reviewers.”

To use Gemini models through Google AI Studio, you'll need an API key. If you don't already have one, create a key in Google AI Studio.

Get an API key here: **[aistudio.google.com](https://aistudio.google.com/app/apikey)**

Once you have your key, provide the key in your .env file, which allows all users of your instance to use it.

```bash filename=".env"
GOOGLE_KEY=mY_SeCreT_w9347w8_kEY
```

Or, you can make users provide it from the frontend by setting the following:

```bash filename=".env"
GOOGLE_KEY=user_provided
```

Some reverse proxies do not support the `X-goog-api-key` header. You can configure LibreChat to use the `Authorization` header instead:

```bash filename=".env"
GOOGLE_AUTH_HEADER=true
```

Since fetching the models list isn't yet supported, you should set the models you want to use in the .env file.

For example, the following current models can be configured for the Gemini API:

```bash filename=".env"
GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite
```

<Callout type="note" title="Notes:">

- Gemini 3.8 Flash, Gemini 3.7 Flash, Gemini 3.6 Flash, Gemini 3.5 Flash, and Gemini 3.5 Flash-Lite are supported through both Google AI Studio and Google Cloud Gemini Enterprise Agent Platform. LibreChat assigns each a 1,048,576-token context window; Gemini 3.8 Flash supports up to 65,536 output tokens.
- LibreChat defaults Gemini 3.8 Flash, Gemini 3.7 Flash, Gemini 3.6 Flash, and Gemini 3.5 Flash to `MEDIUM` thinking, and Gemini 3.5 Flash-Lite to `MINIMAL`. Gemini 3.8 and 3.7 Flash reject `MINIMAL`, so LibreChat substitutes `LOW` when that level is requested; other supported explicit `thinkingLevel` values are preserved.
- For these models, LibreChat removes unsupported sampling and penalty parameters (`temperature`, `topP`, `topK`, `presencePenalty`, and `frequencyPenalty`), ignores the older numeric `thinkingBudget`, and uses `thinkingLevel` instead.
- Gemini Flash models from 3.6 onward do not support assistant prefill. LibreChat prevents edited assistant replies from being resubmitted as an unsupported trailing model-role turn.
- Built-in cost estimates use the Gemini 3.6/3.7/3.8 Flash introductory rates through December 31, 2026: $0.75 input, $3.75 output, and $0.075 cached input per million tokens. From January 1, 2027, the rates are $1.50, $7.50, and $0.15 respectively.
- With the Google endpoint, you cannot use both Vertex AI and Generative Language API at the same time. You must choose one or the other.

</Callout>

Setting `GOOGLE_KEY=user_provided` in your .env file sets both the Vertex AI Service Account JSON key file and the Generative Language API key to be provided from the frontend like so:

![image](https://github.com/danny-avila/LibreChat/assets/110412045/728cbc04-4180-45a8-848c-ae5de2b02996)

## URL Context

Google's URL Context tool lets supported Gemini models read URLs included in the user message, such as web pages, images, and PDFs. In LibreChat, enable it with the Google endpoint parameter `url_context`.

```yaml filename="modelSpecs / list / preset"
preset:
  endpoint: google
  model: gemini-2.5-flash
  url_context: true
```

You can also enable it for a Google-shaped custom endpoint with `addParams` or parameter defaults:

```yaml filename="endpoints / custom"
custom:
  - name: 'Google Gateway'
    apiKey: '${GOOGLE_KEY}'
    baseURL: 'https://gateway.example.com/v1'
    models:
      default: ['gemini-2.5-flash']
    customParams:
      defaultParamsEndpoint: google
    addParams:
      url_context: true
```

`url_context` is available on supported Gemini text models, including Gemini 2.5+ and Gemini 3.x models. YouTube links are handled separately with Gemini's native video understanding when `url_context` is enabled, because Google's URL Context tool does not support YouTube URLs directly.

## Vertex AI

**[See here for Vertex API pricing and rate limits](https://cloud.google.com/vertex-ai/generative-ai/pricing)**

To setup Google LLMs (via Google Cloud Vertex AI), first, signup for Google Cloud: **[cloud.google.com](https://cloud.google.com/)**

You can usually get **$300 starting credit**, which makes this option free for 90 days.

1. Once signed up, Enable the Vertex AI API on Google Cloud:
   - Go to **[Vertex AI page on Google Cloud console](https://console.cloud.google.com/vertex-ai)**
   - Click on `Enable API` if prompted
2. Create a Service Account with Vertex AI role:
   - **[Click here to create a Service Account](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account#step_index=1)**
   - **Select or create a project**
   - Enter a service account ID (required), name and description are optional
     - ![image](https://github.com/danny-avila/LibreChat/assets/110412045/0c5cd177-029b-44fa-a398-a794aeb09de6)
   - Click on "Create and Continue" to give at least the "Vertex AI User" role
     - ![image](https://github.com/danny-avila/LibreChat/assets/110412045/22d3a080-e71e-446e-8485-bcc5bf558dbb)
   - **Click on "Continue/Done"**
3. Create a JSON key to Save in your Project Directory:
   - **Go back to [the Service Accounts page](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts)**
   - **Select your service account**
   - Click on "Keys"
     - ![image](https://github.com/danny-avila/LibreChat/assets/110412045/735a7bbe-25a6-4b4c-9bb5-e0d8aa91be3d)
   - Click on "Add Key" and then "Create new key"
     - ![image](https://github.com/danny-avila/LibreChat/assets/110412045/cfbb20d3-94a8-4cd1-ac39-f9cd8c2fceaa)
   - **Choose JSON as the key type and click on "Create"**
   - **Download the key file and rename it as 'auth.json'**
   - **Save it within the project directory, in `/api/data/`**
     - ![image](https://github.com/danny-avila/LibreChat/assets/110412045/f5b8bcb5-1b20-4751-81a1-d3757a4b3f2f)

<Callout type="info" title="Alternative: Using GOOGLE_SERVICE_KEY_FILE">
  Instead of saving the key file to `/api/data/auth.json`, you can use the `GOOGLE_SERVICE_KEY_FILE`
  environment variable to specify the path to your service account key file. This provides more
  flexibility in how you manage your credentials. See the environment variable section below for
  more details.
</Callout>

**Saving your JSON key file in the project directory which allows all users of your LibreChat instance to use it.**

Alternatively, you can make users provide it from the frontend by setting the following:

```bash filename=".env"
# Note: this configures both the Vertex AI Service Account JSON key file
# and the Generative Language API key to be provided from the frontend.
GOOGLE_KEY=user_provided
```

You can also specify the service account key file using the `GOOGLE_SERVICE_KEY_FILE` environment variable:

```bash filename=".env"
# Path to the service account JSON key file
GOOGLE_SERVICE_KEY_FILE=/path/to/auth.json

# Or provide as a URL
GOOGLE_SERVICE_KEY_FILE=https://example.com/path/to/auth.json

# Or provide as stringified JSON
GOOGLE_SERVICE_KEY_FILE='{"type":"service_account","project_id":"your-project",...}'

# Or provide as base64 encoded JSON
GOOGLE_SERVICE_KEY_FILE=eyJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsICJwcm9qZWN0X2lkIjogInlvdXItcHJvamVjdC1pZCIsIC4uLn0=
```

This is particularly useful for features that require Vertex AI authentication, such as OCR capabilities.

You can also specify the Google Cloud location for Vertex AI API requests:

```bash filename=".env"
# Google Cloud region for Vertex AI
GOOGLE_LOC=us-central1

# Alternative region for Gemini Image Generation (defaults to global)
GOOGLE_CLOUD_LOCATION=global
```

Since fetching the models list isn't yet supported, you should set the models you want to use in the .env file.

For Vertex AI, use the Vertex model IDs where they differ from the Gemini API:

```bash filename=".env"
GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
```

Saved Agents using the `vertexai` provider also use this shared Google model catalog in the Agent Builder and at runtime. If the deployment supplies an explicit `vertexai` model catalog, that provider-specific list takes precedence; otherwise `GOOGLE_MODELS` is used for Vertex AI selection and validation.

<Callout type="note" title="If you are using Docker">
If you're using docker and want to provide the `auth.json` file, you will need to also mount the volume in docker-compose.override.yml

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  api:
    volumes:
      - type: bind
        source: ./api/data/auth.json
        target: /app/api/data/auth.json
```

</Callout>

## Google Safety Settings

To set safety settings for both Vertex AI and Generative Language API, you can set the following in your .env file:

```bash filename=".env"
GOOGLE_SAFETY_SEXUALLY_EXPLICIT=BLOCK_ONLY_HIGH
GOOGLE_SAFETY_HATE_SPEECH=BLOCK_ONLY_HIGH
GOOGLE_SAFETY_HARASSMENT=BLOCK_ONLY_HIGH
GOOGLE_SAFETY_DANGEROUS_CONTENT=BLOCK_ONLY_HIGH
GOOGLE_SAFETY_CIVIC_INTEGRITY=BLOCK_ONLY_HIGH
```

You can also exclude safety settings by setting the following in your .env file, which will use the provider defaults. This can be helpful if you are having issues with specific safety settings.

```bash filename=".env"
GOOGLE_EXCLUDE_SAFETY_SETTINGS=true
```

NOTE: You do not have access to the BLOCK_NONE setting by default.
To use this restricted `HarmBlockThreshold` setting, you will need to either:

- (a) Get access through an allowlist via your Google account team
- (b) Switch your account type to monthly invoiced billing following this instruction:
  https://cloud.google.com/billing/docs/how-to/invoiced-billing

**Notes:**

- Google endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings) via the `librechat.yaml` configuration file, including `streamRate`, `headers`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`


# AWS Bedrock (https://www.librechat.ai/docs/configuration/pre_configured_ai/bedrock)

Head to the [AWS docs](https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html) to sign up for AWS and setup your credentials.

You’ll also need to turn on model access for your account, which you can do by [following these instructions](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html).

## Authentication

Always set the Bedrock region LibreChat should use:

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
```

LibreChat supports the following authentication methods for Bedrock.

### AWS profile

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
BEDROCK_AWS_PROFILE=your-profile-name
```

Use this when you already have credentials in `~/.aws/config` or `~/.aws/credentials`, or when your profile uses AWS IAM Identity Center, role assumption, or `credential_process`.

`BEDROCK_AWS_PROFILE` is a LibreChat-specific setting that passes the selected profile to the AWS SDK credential provider chain for Bedrock. This scopes profile selection to Bedrock without changing credentials used by other integrations. The AWS-standard `AWS_PROFILE` environment variable is still supported by the AWS SDK default provider chain.

If your profile uses `credential_process`, secure the AWS config file and helper command. AWS warns that secret material written to `stderr` can be captured or logged by SDKs and tools.

### Default AWS credential provider chain

You can omit Bedrock-specific credentials and profile settings to let the AWS SDK for JavaScript resolve credentials automatically:

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
```

This is the preferred approach for deployments that use IAM roles or another AWS-native short-term credential source. The SDK checks supported credential providers in precedence order and stops at the first valid credentials it finds. Common sources include environment variables, IAM Identity Center/SSO, shared config and credentials files, web identity, ECS container credentials, EC2 instance metadata, and process credentials.

For example, if AWS-standard `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are set, those credentials can take precedence over profile-based credentials in `~/.aws/credentials` or `~/.aws/config`.

### Bedrock API key

Amazon Bedrock API keys authenticate Bedrock calls with bearer auth instead of SigV4-signed AWS credentials. In LibreChat, configure them with the Bedrock-scoped environment variable:

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
BEDROCK_AWS_BEARER_TOKEN=your_bedrock_api_key
```

`BEDROCK_AWS_BEARER_TOKEN` is LibreChat-specific. AWS documentation and raw AWS SDK/CLI examples use the AWS-standard `AWS_BEARER_TOKEN_BEDROCK` environment variable, but LibreChat intentionally uses a Bedrock-scoped name so the token only affects the Bedrock endpoint configuration. LibreChat passes this value to the AWS SDK as bearer auth.

To let users provide their own Bedrock API key from the LibreChat UI, set:

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
BEDROCK_AWS_BEARER_TOKEN=user_provided
```

Short-term Bedrock API keys inherit the permissions of the AWS principal used to generate them, are valid only in the AWS region where they were generated, and expire no later than 12 hours or the source session expiry. Long-term Bedrock API keys are recommended only for exploration and development. See the AWS docs for [using Bedrock API keys](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-use.html) and [generating Bedrock API keys](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-generate.html).

### Static Bedrock credentials

Use static Bedrock-specific environment variables only when profiles or IAM roles are not suitable:

```bash filename=".env"
BEDROCK_AWS_DEFAULT_REGION=us-east-1
BEDROCK_AWS_ACCESS_KEY_ID=your_access_key_id
BEDROCK_AWS_SECRET_ACCESS_KEY=your_secret_access_key
# BEDROCK_AWS_SESSION_TOKEN=your_session_token
```

If `BEDROCK_AWS_ACCESS_KEY_ID` and `BEDROCK_AWS_SECRET_ACCESS_KEY` are set, LibreChat passes them directly to the Bedrock client. They must be provided together, and they take precedence over `BEDROCK_AWS_PROFILE` and the SDK default provider chain for Bedrock.

If `BEDROCK_AWS_BEARER_TOKEN` is set, LibreChat uses bearer auth for Bedrock instead of static credentials, `BEDROCK_AWS_PROFILE`, or the SDK default provider chain.

For AWS credential behavior details, see the [AWS SDK for JavaScript credential provider chain](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html), the [AWS SDKs and Tools settings reference](https://docs.aws.amazon.com/sdkref/latest/guide/settings-reference.html), and the [AWS `credential_process` security notes](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sourcing-external.html).

## Configuring models

- You can optionally specify which models you want to make available with `BEDROCK_AWS_MODELS`:

```bash filename=".env"
BEDROCK_AWS_MODELS=global.anthropic.claude-fable-5-1,global.anthropic.claude-fable-5,global.anthropic.claude-opus-5,global.anthropic.claude-opus-4-8,global.anthropic.claude-opus-4-7,global.anthropic.claude-sonnet-5,global.anthropic.claude-sonnet-4-6,meta.llama3-1-8b-instruct-v1:0
```

If omitted, LibreChat includes its known supported models automatically. Claude 4 and newer defaults use Bedrock cross-region inference profile IDs because their bare `anthropic.` foundation-model IDs do not support on-demand Converse requests. LibreChat uses `global.` profiles where available and `us.` for Claude Opus 4.1. Use `BEDROCK_AWS_MODELS` to select a different profile available to your account.

- Claude Fable/Mythos-class models on Bedrock are inference-profile only. Use a profile ID such as `global.anthropic.claude-fable-5-1`, and enable Anthropic data sharing in the Bedrock console or Data Retention API before invoking them. Fable 5.1 has a 1M-token context window, 128K-token maximum output, adaptive thinking, and prompt-cache support.

- Claude Opus 5 has a 1M-token context window and 128K-token maximum output. Adaptive thinking is enabled unless explicitly disabled. When thinking is disabled, LibreChat limits `xhigh` or `max` effort to `high` so Bedrock accepts the request.

- Claude Sonnet 5 has a 1M-token context window and 128K-token maximum output. LibreChat applies adaptive thinking to foundation, cross-region inference-profile (for example, `us.anthropic.claude-sonnet-5`), and bare application inference-profile IDs. Turning thinking off sends an explicit disabled setting so that choice persists when a conversation is reloaded.

- See all Bedrock model IDs here:
  - **[https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns](https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns)**

## Additional Configuration

You can further configure the Bedrock endpoint in your [`librechat.yaml`](/docs/configuration/librechat_yaml/) file:

```yaml
endpoints:
  bedrock:
    availableRegions:
      - 'us-east-1'
      - 'us-west-2'
    streamRate: 35
    titleModel: 'anthropic.claude-3-haiku-20240307-v1:0'
    guardrailConfig:
      guardrailIdentifier: 'abc123xyz'
      guardrailVersion: '1'
      trace: 'enabled'
      streamProcessingMode: 'sync'
```

- `streamRate`: (Optional) Set the rate of processing each new token in milliseconds.
  - This can help stabilize processing of concurrent requests and provide smoother frontend stream rendering.

- `titleModel`: (Optional) Specify the model to use for generating conversation titles.
  - Recommended: `anthropic.claude-3-haiku-20240307-v1:0`.
  - Omit or set as `current_model` to use the same model as the chat.

- `availableRegions`: (Optional) Specify the AWS regions you want to make available.
  - If provided, users will see a dropdown to select the region. If not selected, the default region is used.
  - ![image](https://github.com/user-attachments/assets/6f3c5e82-9c6b-4643-8487-07db1061ba49)

- `guardrailConfig`: (Optional) Configure AWS Bedrock Guardrails for content filtering.
  - `guardrailIdentifier`: The guardrail ID or ARN from your AWS Bedrock Console.
  - `guardrailVersion`: The guardrail version number (e.g., `"1"`) or `"DRAFT"`.
  - `trace`: (Optional) Enable trace logging: `"enabled"`, `"disabled"`, or `"enabled_full"`.
  - `streamProcessingMode`: (Optional) Set stream processing mode: `"sync"` or `"async"` (defaults to `"sync"`).
  - See [AWS Bedrock Guardrails documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-how.html) for creating and managing guardrails.

## Inference Profiles

AWS Bedrock inference profiles let you create custom routing configurations for foundation models, enabling cross-region load balancing, cost allocation, and compliance controls. You can map model IDs to custom inference profile ARNs in your `librechat.yaml`:

```yaml
endpoints:
  bedrock:
    inferenceProfiles:
      'us.anthropic.claude-3-7-sonnet-20250219-v1:0': '${BEDROCK_CLAUDE_37_PROFILE}'
```

For the full guide on creating profiles, configuring LibreChat, setting up logging, and troubleshooting, see **[Bedrock Inference Profiles](/docs/configuration/pre_configured_ai/bedrock_inference_profiles)**.

For the YAML field reference, see **[AWS Bedrock Object Structure](/docs/configuration/librechat_yaml/object_structure/aws_bedrock#inferenceprofiles)**.

## Document Uploads

Bedrock supports uploading documents directly to the provider via the `Upload to Provider` option in the file attachment dropdown menu. Documents are sent to the Bedrock Converse API as native document attachments.

**Supported formats:** PDF, CSV, DOC, DOCX, XLS, XLSX, HTML, TXT, and Markdown (.md)

**Limitations:**

- The default maximum file size is **4.5 MB**.
- When `fileConfig` does not set a smaller limit, Claude 4+ PDFs and Amazon Nova PDFs or DOCX files can be up to **32 MB**.
- File names are automatically sanitized to conform to Bedrock's naming requirements (alphanumeric, spaces, hyphens, parentheses, square brackets; max 200 characters)

For more information on file upload options, see the [OCR for Documents](/docs/features/ocr#5-upload-files-to-provider-direct) documentation.

## Notes

- The following models are not supported due to lack of streaming capability:
  - ai21.j2-mid-v1

- The following models are not supported due to lack of conversation history support:
  - ai21.j2-ultra-v1
  - cohere.command-text-v14
  - cohere.command-light-text-v14

- AWS Bedrock endpoint supports all [Shared Endpoint Settings](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings) via the `librechat.yaml` configuration file, including `streamRate`, `titleModel`, `titleMethod`, `titlePrompt`, `titlePromptTemplate`, and `titleEndpoint`


# Bedrock Inference Profiles (https://www.librechat.ai/docs/configuration/pre_configured_ai/bedrock_inference_profiles)

This guide explains how to configure and use AWS Bedrock custom inference profiles with LibreChat, allowing you to route model requests through custom application inference profiles for better control, cost allocation, and cross-region load balancing.

## Overview

AWS Bedrock inference profiles allow you to create custom routing configurations for foundation models. When you create a custom (application) inference profile, AWS generates a unique ARN that doesn't contain model name information:

```
arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123def456
```

LibreChat's inference profile mapping feature allows you to:

1. Map friendly model IDs to custom inference profile ARNs
2. Route requests through your custom profiles while maintaining model capability detection
3. Use environment variables for secure ARN management

## Why Use Custom Inference Profiles?

| Benefit                         | Description                                                   |
| ------------------------------- | ------------------------------------------------------------- |
| **Cross-Region Load Balancing** | Automatically distribute requests across multiple AWS regions |
| **Cost Allocation**             | Tag and track costs per application or team                   |
| **Throughput Management**       | Configure dedicated throughput for your applications          |
| **Compliance**                  | Route requests through specific regions for data residency    |
| **Monitoring**                  | Track usage per inference profile in CloudWatch               |

## Prerequisites

Before you begin, ensure you have:

1. **AWS Account** with Bedrock access enabled
2. **AWS CLI** installed and configured
3. **IAM Permissions**:
   - `bedrock:CreateInferenceProfile`
   - `bedrock:ListInferenceProfiles`
   - `bedrock:GetInferenceProfile`
   - `bedrock:InvokeModel` / `bedrock:InvokeModelWithResponseStream`
4. **LibreChat** with Bedrock endpoint configured (see [AWS Bedrock Setup](/docs/configuration/pre_configured_ai/bedrock))

## Creating Custom Inference Profiles

> **Important**: Custom inference profiles can only be created via API (AWS CLI, SDK, etc.) and cannot be created from the AWS Console.

### Method 1: AWS CLI (Recommended)

#### Step 1: List Available System Inference Profiles

```bash
# List all inference profiles
aws bedrock list-inference-profiles --region us-east-1

# Filter for Claude models
aws bedrock list-inference-profiles --region us-east-1 \
  --query "inferenceProfileSummaries[?contains(inferenceProfileId, 'claude')]"
```

#### Step 2: Create a Custom Inference Profile

```bash
# Get the system inference profile ARN to copy from
export SOURCE_PROFILE_ARN=$(aws bedrock list-inference-profiles --region us-east-1 \
  --query "inferenceProfileSummaries[?inferenceProfileId=='us.anthropic.claude-3-7-sonnet-20250219-v1:0'].inferenceProfileArn" \
  --output text)

# Create your custom inference profile
aws bedrock create-inference-profile \
  --inference-profile-name "MyApp-Claude-3-7-Sonnet" \
  --description "Custom inference profile for my application" \
  --model-source copyFrom="$SOURCE_PROFILE_ARN" \
  --region us-east-1
```

#### Step 3: Verify Creation

```bash
# List your custom profiles
aws bedrock list-inference-profiles --type-equals APPLICATION --region us-east-1

# Get details of a specific profile
aws bedrock get-inference-profile \
  --inference-profile-identifier "arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123" \
  --region us-east-1
```

### Method 2: Python Script

```python
import boto3

AWS_REGION = 'us-east-1'

def create_inference_profile(profile_name: str, source_model_id: str):
    """
    Create a custom inference profile for LibreChat.

    Args:
        profile_name: Name for your custom profile
        source_model_id: The system inference profile ID to copy from
                        (e.g., 'us.anthropic.claude-3-7-sonnet-20250219-v1:0')
    """
    bedrock = boto3.client('bedrock', region_name=AWS_REGION)

    profiles = bedrock.list_inference_profiles()
    source_arn = None
    for profile in profiles['inferenceProfileSummaries']:
        if profile['inferenceProfileId'] == source_model_id:
            source_arn = profile['inferenceProfileArn']
            break

    if not source_arn:
        raise ValueError(f"Source profile {source_model_id} not found")

    response = bedrock.create_inference_profile(
        inferenceProfileName=profile_name,
        description=f'Custom inference profile for {profile_name}',
        modelSource={'copyFrom': source_arn},
        tags=[
            {'key': 'Application', 'value': 'LibreChat'},
            {'key': 'Environment', 'value': 'Production'}
        ]
    )

    print(f"Created profile: {response['inferenceProfileArn']}")
    return response['inferenceProfileArn']

if __name__ == "__main__":
    create_inference_profile(
        "LibreChat-Claude-3-7-Sonnet",
        "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
    )
    create_inference_profile(
        "LibreChat-Claude-Sonnet-4-5",
        "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
    )
```

## Configuring LibreChat

### librechat.yaml Configuration

Add the `bedrock` endpoint configuration to your `librechat.yaml`. For full field reference, see [AWS Bedrock Object Structure](/docs/configuration/librechat_yaml/object_structure/aws_bedrock).

```yaml filename="librechat.yaml"
endpoints:
  bedrock:
    # List the models you want available in the UI
    models:
      - 'us.anthropic.claude-3-7-sonnet-20250219-v1:0'
      - 'us.anthropic.claude-sonnet-4-5-20250929-v1:0'
      - 'global.anthropic.claude-opus-4-5-20251101-v1:0'
    # Map model IDs to their custom inference profile ARNs
    inferenceProfiles:
      # Using environment variable (recommended for security)
      'us.anthropic.claude-3-7-sonnet-20250219-v1:0': '${BEDROCK_CLAUDE_37_PROFILE}'
      # Using direct ARN
      'us.anthropic.claude-sonnet-4-5-20250929-v1:0': 'arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123'
      # Another env variable example
      'global.anthropic.claude-opus-4-5-20251101-v1:0': '${BEDROCK_OPUS_45_PROFILE}'
    # Optional: Configure available regions for cross-region inference
    availableRegions:
      - 'us-east-1'
      - 'us-west-2'
```

### Environment Variables

Add your Bedrock region, AWS authentication settings, and inference profile ARNs to your `.env` file:

```bash filename=".env"
#===================================#
# AWS Bedrock Configuration         #
#===================================#

BEDROCK_AWS_DEFAULT_REGION=us-east-1

# Option 1: Use an AWS profile
BEDROCK_AWS_PROFILE=your-profile-name

# Option 2: Omit BEDROCK_AWS_PROFILE and Bedrock-specific static credentials
# to use the AWS SDK default credential provider chain.

# Option 3: Static Bedrock credentials, if profiles or IAM roles are not suitable
# BEDROCK_AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
# BEDROCK_AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# BEDROCK_AWS_SESSION_TOKEN=your-session-token

# Option 4: Bedrock API key (bearer auth)
# BEDROCK_AWS_BEARER_TOKEN=your-bedrock-api-key

# Inference Profile ARNs
BEDROCK_CLAUDE_37_PROFILE=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123
BEDROCK_OPUS_45_PROFILE=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/def456
```

## Setting Up Logging

To verify that your inference profiles are being used correctly, enable AWS Bedrock model invocation logging.

### 1. Create CloudWatch Log Group

```bash
aws logs create-log-group \
  --log-group-name /aws/bedrock/model-invocations \
  --region us-east-1
```

### 2. Create IAM Role for Bedrock Logging

Create the trust policy file (`bedrock-logging-trust.json`):

```json filename="bedrock-logging-trust.json"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "bedrock.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "YOUR_ACCOUNT_ID"
        },
        "ArnLike": {
          "aws:SourceArn": "arn:aws:bedrock:us-east-1:YOUR_ACCOUNT_ID:*"
        }
      }
    }
  ]
}
```

Create the role:

```bash
aws iam create-role \
  --role-name BedrockLoggingRole \
  --assume-role-policy-document file://bedrock-logging-trust.json
```

Attach CloudWatch Logs permissions:

```bash
aws iam put-role-policy \
  --role-name BedrockLoggingRole \
  --policy-name BedrockLoggingPolicy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ],
        "Resource": "arn:aws:logs:us-east-1:YOUR_ACCOUNT_ID:log-group:/aws/bedrock/model-invocations:*"
      }
    ]
  }'
```

Create S3 bucket for large data (required):

```bash
aws s3 mb s3://bedrock-logs-YOUR_ACCOUNT_ID --region us-east-1

aws iam put-role-policy \
  --role-name BedrockLoggingRole \
  --policy-name BedrockS3Policy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": ["s3:PutObject"],
        "Resource": "arn:aws:s3:::bedrock-logs-YOUR_ACCOUNT_ID/*"
      }
    ]
  }'
```

### 3. Enable Model Invocation Logging

```bash
aws bedrock put-model-invocation-logging-configuration \
  --logging-config '{
    "cloudWatchConfig": {
      "logGroupName": "/aws/bedrock/model-invocations",
      "roleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/BedrockLoggingRole",
      "largeDataDeliveryS3Config": {
        "bucketName": "bedrock-logs-YOUR_ACCOUNT_ID",
        "keyPrefix": "large-data"
      }
    },
    "textDataDeliveryEnabled": true,
    "imageDataDeliveryEnabled": true,
    "embeddingDataDeliveryEnabled": true
  }' \
  --region us-east-1
```

Verify logging is enabled:

```bash
aws bedrock get-model-invocation-logging-configuration --region us-east-1
```

## Verifying Your Configuration

### View Logs via CLI

After making a request through LibreChat, check the logs:

```bash
# Tail logs in real-time
aws logs tail /aws/bedrock/model-invocations --follow --region us-east-1

# View recent logs
aws logs tail /aws/bedrock/model-invocations --since 5m --region us-east-1
```

### What to Look For

In the log output, look for the `modelId` field:

```json
{
  "timestamp": "2026-01-16T16:56:15Z",
  "accountId": "123456789012",
  "region": "us-east-1",
  "requestId": "a8b9d8c9-87b3-41ea-8a02-e8bfdba7782f",
  "operation": "ConverseStream",
  "modelId": "arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123",
  "inferenceRegion": "us-west-2"
}
```

**Success indicators:**

- `modelId` shows your custom inference profile ARN (contains `application-inference-profile`)
- `inferenceRegion` may vary (shows cross-region routing is working)

**If mapping isn't working:**

- `modelId` will show the raw model ID instead of the ARN

### View Logs via AWS Console

1. Open **CloudWatch** in the AWS Console
2. Navigate to **Logs** > **Log groups**
3. Select `/aws/bedrock/model-invocations`
4. Click on the latest log stream
5. Search for your inference profile ID

## Monitoring Usage

### CloudWatch Metrics

View Bedrock metrics in CloudWatch:

```bash
aws cloudwatch list-metrics --namespace AWS/Bedrock --region us-east-1
```

### AWS Console

1. **Bedrock Console** > **Inference profiles** > **Application** tab
2. Click on your custom profile
3. View invocation metrics and usage statistics

## Troubleshooting

### Common Issues

| Issue                     | Cause                                  | Solution                                                                      |
| ------------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
| Model not recognized      | Missing model in `models` array        | Add the model ID to `models` in librechat.yaml                                |
| ARN not being used        | Model ID doesn't match                 | Ensure the model ID in `inferenceProfiles` exactly matches what's in `models` |
| Env variable not resolved | Typo or not set                        | Check `.env` file and ensure variable name matches `${VAR_NAME}`              |
| Access Denied             | Missing IAM permissions                | Add `bedrock:InvokeModel*` permissions for the inference profile ARN          |
| Model access denied       | Model agreement missing or propagating | Accept the Bedrock model agreement and wait for availability to propagate     |
| Profile not found         | Wrong region                           | Ensure you're creating/using profiles in the same region                      |

### Model Access Agreement Propagation

Creating an application inference profile does not automatically enable the underlying foundation model in your AWS account. If model access was just enabled, AWS may also need a short propagation window before requests through the inference profile succeed.

This can appear as an `AccessDeniedException` even when the inference profile exists and your IAM role has `bedrock:InvokeModel` permissions. The error may mention `aws-marketplace:ViewSubscriptions`, `aws-marketplace:Subscribe`, or ask you to try again after a few minutes.

Check the underlying model availability before debugging the LibreChat mapping:

```bash
aws bedrock get-foundation-model-availability \
  --region us-east-1 \
  --model-id us.anthropic.claude-sonnet-4-5-20250929-v1:0
```

Look for:

- `agreementAvailability.status` set to `AVAILABLE`
- `authorizationStatus` set to `AUTHORIZED`
- `entitlementAvailability` set to `AVAILABLE`
- `regionAvailability` set to `AVAILABLE`

If the agreement is missing, accept the model agreement in the Bedrock console or with an AWS principal that can manage Bedrock model agreements and Marketplace subscriptions. After it changes to `AVAILABLE`, wait a couple of minutes and retry invoking the application inference profile.

### Debug Checklist

1. Model ID is in the `models` array
2. Model ID in `inferenceProfiles` exactly matches (case-sensitive)
3. Environment variable is set (if using `${VAR}` syntax)
4. AWS credentials have permission to invoke the inference profile
5. The underlying foundation model agreement is `AVAILABLE` in Bedrock
6. LibreChat has been restarted after config changes

### Verify Config Loading

Check that your config is being read correctly by examining the server logs when LibreChat starts.

## Complete Example

### librechat.yaml

```yaml filename="librechat.yaml"
version: 1.3.5

endpoints:
  bedrock:
    models:
      - 'us.anthropic.claude-3-7-sonnet-20250219-v1:0'
      - 'us.anthropic.claude-sonnet-4-5-20250929-v1:0'
      - 'global.anthropic.claude-opus-4-5-20251101-v1:0'
      - 'us.amazon.nova-pro-v1:0'
    inferenceProfiles:
      'us.anthropic.claude-3-7-sonnet-20250219-v1:0': '${BEDROCK_CLAUDE_37_PROFILE}'
      'us.anthropic.claude-sonnet-4-5-20250929-v1:0': '${BEDROCK_SONNET_45_PROFILE}'
      'global.anthropic.claude-opus-4-5-20251101-v1:0': '${BEDROCK_OPUS_45_PROFILE}'
    availableRegions:
      - 'us-east-1'
      - 'us-west-2'
```

### .env

```bash filename=".env"
# AWS Bedrock
BEDROCK_AWS_DEFAULT_REGION=us-east-1
BEDROCK_AWS_PROFILE=your-profile-name
# Or use a Bedrock API key instead:
# BEDROCK_AWS_BEARER_TOKEN=your-bedrock-api-key

# Inference Profiles
BEDROCK_CLAUDE_37_PROFILE=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123
BEDROCK_SONNET_45_PROFILE=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/def456
BEDROCK_OPUS_45_PROFILE=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/ghi789
```

### Quick Setup Script

```bash filename="setup-bedrock-profiles.sh"
#!/bin/bash

REGION="us-east-1"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Create inference profiles
for MODEL in "us.anthropic.claude-3-7-sonnet-20250219-v1:0" "us.anthropic.claude-sonnet-4-5-20250929-v1:0"; do
  PROFILE_NAME="LibreChat-${MODEL//[.:]/-}"
  SOURCE_ARN=$(aws bedrock list-inference-profiles --region $REGION \
    --query "inferenceProfileSummaries[?inferenceProfileId=='$MODEL'].inferenceProfileArn" \
    --output text)
  if [ -n "$SOURCE_ARN" ]; then
    echo "Creating profile for $MODEL..."
    aws bedrock create-inference-profile \
      --inference-profile-name "$PROFILE_NAME" \
      --model-source copyFrom="$SOURCE_ARN" \
      --region $REGION
  fi
done

# List created profiles
echo ""
echo "Your custom inference profiles:"
aws bedrock list-inference-profiles --type-equals APPLICATION --region $REGION \
  --query "inferenceProfileSummaries[].{Name:inferenceProfileName,ARN:inferenceProfileArn}" \
  --output table
```

## Related Resources

- [AWS Bedrock Inference Profiles Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles.html)
- [AWS Bedrock Model Access Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html)
- [AWS Bedrock Object Structure](/docs/configuration/librechat_yaml/object_structure/aws_bedrock) - YAML config field reference
- [AWS Bedrock Setup](/docs/configuration/pre_configured_ai/bedrock) - Basic Bedrock configuration
- [AWS Bedrock Model Invocation Logging](https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html)


# Assistants (https://www.librechat.ai/docs/configuration/pre_configured_ai/assistants)

- The [Assistants API by OpenAI](https://platform.openai.com/docs/assistants/overview) has a dedicated endpoint.
- The Assistants API enables the creation of AI assistants, offering functionalities like code interpreter, knowledge retrieval of files, and function execution.
    - [Read here for an in-depth documentation](https://platform.openai.com/docs/assistants/overview) of the feature, how it works, what it's capable of.
- As with the regular [OpenAI API](/docs/configuration/pre_configured_ai/openai), go to **[https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys)** to get a key.
- You will need to set the following environment variable to your key or you can set it to `user_provided` for users to provide their own.

```bash filename=".env"
ASSISTANTS_API_KEY=your-key
```

- You can determine which models you would like to have available with `ASSISTANTS_MODELS`; otherwise, the models list fetched from OpenAI will be used (only Assistants API compatible models will be shown).

```bash filename=".env"
ASSISTANTS_MODELS=gpt-3.5-turbo-0125,gpt-3.5-turbo-16k-0613,gpt-3.5-turbo-16k,gpt-3.5-turbo,gpt-4,gpt-4-0314,gpt-4-32k-0314,gpt-4-0613,gpt-3.5-turbo-0613,gpt-3.5-turbo-1106,gpt-4-0125-preview,gpt-4-turbo-preview,gpt-4-1106-preview
```

- If necessary, you can also set an alternate base URL instead of the official one with `ASSISTANTS_BASE_URL`, which is similar to the OpenAI counterpart `OPENAI_REVERSE_PROXY`

```bash filename=".env"
ASSISTANTS_BASE_URL=http://your-alt-baseURL:3080/
```

- There is additional, optional configuration, depending on your needs, such as disabling the assistant builder UI, that are available via the `librechat.yaml` [custom config file](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint):
    - Control the visibility and use of the builder interface for assistants. [More info](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint#disablebuilder)
    - Specify the polling interval in milliseconds for checking run updates or changes in assistant run states. [More info](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint#pollintervalms)
    - Set the timeout period in milliseconds for assistant runs. Helps manage system load by limiting total run operation time. [More info](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint#timeoutms)
    - Specify which assistant Ids are supported or excluded [More info](/docs/configuration/librechat_yaml/object_structure/assistants_endpoint#supportedids)

## Strict function calling
With librechat you can add add the 'x-strict': true flag at operation-level in the openapi spec for actions.
This will automatically generate function calls with 'strict' mode enabled.
Note that strict mode supports only a partial subset of json. Read https://platform.openai.com/docs/guides/structured-outputs for details.

For example:
```json filename="mathapi.json"
{
  "openapi": "3.1.0",
  "info": {
    "title": "Math.js API",
    "description": "API for performing mathematical operations, such as addition, subtraction, etc.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.mathjs.org/v4"
    }
  ],
  "paths": {
    "/": {
      "post": {
        "summary": "Evaluate a mathematical expression",
        "description": "Sends a mathematical expression in the request body to evaluate.",
"operationId": "math",
"x-strict": true,
"parameters": [
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expr": {
                    "type": "string",
                    "description": "The mathematical expression to evaluate (e.g., `2+3`)."
                  }
                },
                "required": ["expr"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The result of the evaluated expression.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "number",
                      "description": "The evaluated result of the expression."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid expression provided.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Error message describing the invalid expression."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

<Callout type="note" title="Notes">
**Notes:**
- At the time of writing, only the following models support the [Retrieval](https://platform.openai.com/docs/assistants/tools/knowledge-retrieval) capability:
    - gpt-3.5-turbo-0125
    - gpt-4-0125-preview
    - gpt-4-turbo-preview
    - gpt-4-1106-preview
    - gpt-3.5-turbo-1106
- Vision capability is not yet supported.
- If you have previously set the [`ENDPOINTS` value in your .env file](./dotenv.md#endpoints), you will need to add the value `assistants`
</Callout>



# Tools (https://www.librechat.ai/docs/configuration/tools)

LibreChat tools are selected from the **Agent Builder** and run when an agent decides they are useful. This section covers built-in agent tools such as image generation, search, weather, computation, and private index lookup.

<Callout type="info" title="Not the same as Web Search or MCP">

The search tools on this page are tools you add to a specific agent. LibreChat's built-in [Web Search](/docs/features/web_search) feature is configured separately, and custom third-party tools are usually added through [MCP](/docs/features/mcp) or [Actions](/docs/features/agents#actions).

</Callout>

## Quick Setup

<Steps>
  <Step>

### Pick the Tool

Choose a tool from the table below and collect any required API keys, service URLs, or index names.

  </Step>
  <Step>

### Add Credentials

Add the required values to your `.env` file, or let users provide their own credentials from the LibreChat UI when the tool prompts for them.

  </Step>
  <Step>

### Restart LibreChat

Environment variable changes are loaded on restart.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

  </Step>
  <Step>

### Add the Tool to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select the tool, and save the agent.

  </Step>
  <Step>

### Test in Chat

Start a chat with that agent and ask for something that requires the tool, such as a search, calculation, weather report, or image.

  </Step>
</Steps>

## Current Built-In Tools

| Tool | Use it for | Required configuration | Details |
|------|------------|------------------------|---------|
| OpenAI Image Tools | Generate and edit images with OpenAI image models | `IMAGE_GEN_OAI_API_KEY`; optional `IMAGE_GEN_OAI_MODEL` | [Image Generation](/docs/features/image_gen#1--openai-image-tools-recommended) |
| Gemini Image Tools | Generate images and edit with image context using Gemini | `GEMINI_API_KEY`, `GOOGLE_KEY`, or `GOOGLE_SERVICE_KEY_FILE`; optional `GEMINI_IMAGE_MODEL` | [Gemini Image Generation](/docs/configuration/tools/gemini_image_gen) |
| DALL-E-3 | Legacy OpenAI image generation | `DALLE3_API_KEY` or `DALLE_API_KEY` | [DALL-E](/docs/features/image_gen#3--dalle-legacy) |
| Flux | Cloud image generation and fine-tuned image models | `FLUX_API_KEY`; optional `FLUX_API_BASE_URL` | [Flux](/docs/configuration/tools/flux) |
| Stable Diffusion | Local or self-hosted image generation through Automatic1111 | `SD_WEBUI_URL` | [Stable Diffusion](/docs/configuration/tools/stable_diffusion) |
| Google Search | Google Custom Search results for an agent | `GOOGLE_SEARCH_API_KEY` and `GOOGLE_CSE_ID` | [Google Search](/docs/configuration/tools/google_search) |
| Tavily Search | Current web results optimized for agents | `TAVILY_API_KEY` | [Tavily Search](/docs/configuration/tools/tavily) |
| Traversaal | AI search results with sources | `TRAVERSAAL_API_KEY` | [Traversaal](/docs/configuration/tools/traversaal) |
| Azure AI Search | Search a private Azure AI Search index | `AZURE_AI_SEARCH_SERVICE_ENDPOINT`, `AZURE_AI_SEARCH_INDEX_NAME`, `AZURE_AI_SEARCH_API_KEY` | [Azure AI Search](/docs/configuration/tools/azure_ai_search) |
| OpenWeather | Current, forecast, historical, and daily weather data | `OPENWEATHER_API_KEY` | [OpenWeather](/docs/configuration/tools/openweather) |
| Wolfram\|Alpha | Math, computation, units, curated knowledge, and real-time data | `WOLFRAM_APP_ID` | [Wolfram\|Alpha](/docs/configuration/tools/wolfram) |
| Calculator | Basic and complex calculations | None | [Calculator](/docs/configuration/tools/calculator) |

## Creating Custom Tools

Most custom tools should be added without editing LibreChat source code.

- Use [MCP](/docs/features/mcp) when you want to connect an agent to a local script, internal service, database, browser automation server, or a custom API wrapper. For example, a DuckDuckGo search tool can be exposed through an MCP server and then selected from the Agent Builder.
- Use [Actions](/docs/features/agents#actions) when the tool is an HTTP API that can be described with an OpenAPI schema.
- Edit LibreChat's source-level structured tools only when you are developing LibreChat itself. The legacy [Tools and Plugins development guide](/docs/development/tools_and_plugins) remains available for contributors, but MCP and Actions are the recommended extension paths for deployments.

## Tool Availability

Tools are identified internally by their `pluginKey` from LibreChat's `api/app/clients/tools/manifest.json`.

Use [`filteredTools`](/docs/configuration/librechat_yaml/object_structure/config#filteredtools) to hide tools, or [`includedTools`](/docs/configuration/librechat_yaml/object_structure/config#includedtools) to allow only specific tools:

```yaml filename="librechat.yaml"
includedTools:
  - calculator
  - image_gen_oai
  - google
```

If a tool is not visible in the Agent Builder after restart, check the tool's environment variables, `includedTools`, `filteredTools`, and whether the agent's `tools` capability is enabled.


# Flux Image Generation (https://www.librechat.ai/docs/configuration/tools/flux)

Flux is a powerful image generation tool that can create high-quality images from text descriptions. It supports various artistic styles and offers extensive customization options.

## Setup Instructions

1. Get your API key from [bfl.ml](https://bfl.ml)
2. Set the `FLUX_API_KEY` environment variable in your `.env` file:

```bash
FLUX_API_KEY=your_api_key_here
```

3. Restart LibreChat and add **Flux** to an agent's **Tools** list.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

## Features

### Core Capabilities
- Generate high-quality images from detailed text descriptions
- Support for multiple artistic styles
- Customizable image dimensions
- Adjustable generation parameters
- Multiple endpoint options for different use cases
- Batch generation support (up to 24 images)

### Available Endpoints
- `/v1/flux-pro` - Standard endpoint (default)
- `/v1/flux-pro-1.1` - Enhanced version
- `/v1/flux-dev` - Development version
- `/v1/flux-pro-1.1-ultra` - Premium quality endpoint

### Parameters

The Flux tool supports three main actions:

1. **generate** - Create a new image from a text prompt
2. **generate_finetuned** - Create an image using a fine-tuned model
3. **list_finetunes** - List available custom models for the user


For `generate` action:

• **prompt** – Text description for the image (required)  
• **width** – Width in pixels (multiple of 32)  
• **height** – Height in pixels (multiple of 32)  
• **prompt_upsampling** – Whether to perform upsampling on the prompt (default: false)  
• **steps** – Number of diffusion steps (1-50, default: 40)  
• **seed** – Optional seed for reproducibility  
• **safety_tolerance** – Tolerance level for moderation (0-6, default: 6)  
• **endpoint** – Model endpoint to use:
  - `/v1/flux-pro-1.1` (default)
  - `/v1/flux-pro`
  - `/v1/flux-dev`
  - `/v1/flux-pro-1.1-ultra`
• **raw** – Generate less processed images (only for ultra endpoint, default: false)

For `generate_finetuned` action:

• All parameters from `generate` plus:  
• **finetune_id** – ID of the fine-tuned model (required)  
• **finetune_strength** – Strength of the fine-tuning effect (0.1-1.2, default: 1.1)  
• **guidance** – Guidance scale (default: 2.5)  
• **aspect_ratio** – Aspect ratio for ultra models (default: "16:9")  
• **endpoint** – Must be one of:
  - `/v1/flux-pro-finetuned` (default)
  - `/v1/flux-pro-1.1-ultra-finetuned`


## Best Practices

### Prompt Writing
1. Be specific and detailed in descriptions
2. Include key elements:
   - Subject matter
   - Style and artistic approach
   - Composition details
   - Lighting and atmosphere
   - Color preferences
   - Technical specifications

### Tips for Best Results
- Write prompts in English
- Balance specificity with creative freedom
- Avoid conflicting concepts
- Focus on visual descriptions
- Consider composition layers (foreground, middle ground, background)

## Technical Details

### Image Processing
- Images are automatically saved and managed
- Supports PNG output format
- Includes built-in moderation and safety features
- Asynchronous generation with status tracking

### Integration Features
- Seamless integration with chat interfaces
- Markdown-formatted output
- Built-in error handling and logging
- Support for batch processing

## Usage Examples

Here are some example prompts that work well with Flux:

> A serene mountain landscape at sunset, with snow-capped peaks reflected in a crystal-clear alpine lake. Warm golden light illuminates wispy clouds, creating a dramatic atmosphere. Photorealistic style with rich colors and sharp details.

> A futuristic cityscape at night, featuring neon-lit skyscrapers and flying vehicles. Cyberpunk style with deep blues and purples, accented by bright neon colors. Rain-slicked streets reflect the city lights, creating a moody atmosphere.

## Error Handling

Common error messages and solutions:
- API key issues: Verify your API key is correctly set in environment variables
- Generation failures: Check prompt length and content guidelines
- Timeout errors: May occur during high server load, retry after a brief wait

## Rate Limits and Usage

- Free tier includes generous usage limits
- Multiple images can be generated in a single request
- Consider using lower step counts for faster generations during testing


# Gemini Image Generation (https://www.librechat.ai/docs/configuration/tools/gemini_image_gen)

Gemini Image Generation is a powerful tool that integrates Google's Gemini Image Models for high-quality text-to-image generation and image context-aware editing. It supports both the simple Gemini API and Google Cloud Vertex AI.

## Setup Instructions

You can use either the Gemini API (recommended for most users) or Vertex AI with a service account.

### Option 1: Gemini API (Recommended)

1. Get your API key from [Google AI Studio](https://aistudio.google.com/app/apikey)
2. Set the `GEMINI_API_KEY` environment variable in your `.env` file:

```bash
GEMINI_API_KEY=your_api_key_here
```

### Option 2: Vertex AI (For Enterprise/GCP Users)

1. Create a service account in Google Cloud Console with Vertex AI permissions
2. Download the service account JSON key file
3. Place the JSON file in the project (e.g., `api/data/auth.json`) or set the path:

```bash
# Path to your service account JSON file (default: api/data/auth.json)
GOOGLE_SERVICE_KEY_FILE=/path/to/service-account.json

# Optional: Set the location (default: global)
GOOGLE_CLOUD_LOCATION=us-central1
```

When no `GEMINI_API_KEY` or `GOOGLE_KEY` is configured, the tool automatically falls back to Vertex AI using the service account file.

After configuring credentials, restart LibreChat and add **Gemini Image Tools** to an agent's **Tools** list.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

## Configuration Options

### Model Selection

You can choose which Gemini image model to use via environment variable:

```bash
# Default model
GEMINI_IMAGE_MODEL=gemini-2.5-flash-image

# Or use the newer Gemini 3 Pro Image model
GEMINI_IMAGE_MODEL=gemini-3-pro-image-preview
```

### Available Models

| Model | Description |
|-------|-------------|
| `gemini-2.5-flash-image` | Default model, fast and efficient |
| `gemini-3-pro-image-preview` | Higher quality, more detailed generations |

## Features

### Core Capabilities

- **Text-to-Image Generation**: Create images from detailed text descriptions
- **Image Context Support**: Use existing images as context/inspiration for new generations
- **Image Editing**: Generate new images based on modifications to existing ones
- **Safety Filtering**: Built-in content safety with user-friendly error messages

### Parameters

The Gemini Image Gen tool accepts the following parameters:

- **prompt** (required) – A detailed text description of the desired image, up to 32,000 characters
- **image_ids** (optional) – Array of image IDs to use as visual context for generation

## Best Practices

### Prompt Writing

1. **Be specific and detailed** in your descriptions
2. **Start with the image type**: photo, oil painting, watercolor, illustration, cartoon, drawing, vector, render, etc.
3. **Include key elements**:
   - Subject matter and composition
   - Style and artistic approach
   - Lighting and atmosphere
   - Color palette preferences
   - Technical specifications

### Image Editing Tips

When editing existing images:

1. **Include the original image ID** in the `image_ids` array
2. **Use direct editing instructions**:
   - "Remove the background from this image"
   - "Add sunglasses to the person in this image"
   - "Change the color of the car to red"
3. **Don't reconstruct the original prompt** – use simple, direct modification instructions

## Usage Examples

### Basic Image Generation

> A serene Japanese garden at golden hour, featuring a traditional red bridge over a koi pond. Cherry blossom trees frame the scene with soft pink petals falling. Photorealistic style with warm, diffused lighting and rich colors.

### Image with Context

When you have an existing image and want to create something inspired by it:

1. Reference the image ID in the `image_ids` parameter
2. Describe what you want: "Create a winter version of this landscape scene with snow-covered trees and a frozen lake"

### Image Editing

To modify an existing image:

1. Include the image ID in `image_ids`
2. Describe the change: "Remove the person from the background of this image"

## Error Handling

### Common Issues

| Error | Solution |
|-------|----------|
| "Image blocked by content safety filters" | Modify your prompt to avoid content that violates safety policies |
| "No image was generated" | Try a different prompt or simplify your request |
| "GEMINI_API_KEY or service account required" | Ensure you've configured either the API key or Vertex AI credentials |

### Safety Filtering

Gemini includes built-in safety filters. If your image is blocked:

- Review your prompt for potentially problematic content
- Try rephrasing to be more specific about artistic intent
- Avoid requests for harmful, violent, or explicit content

## Technical Details

### Storage Integration

Generated images are automatically saved using your configured file strategy (local, S3, Azure, or Firebase). This is handled by the framework — the tool returns image data and the agent callback system persists it as a message attachment.

### Image Format

- Output format defaults to PNG, configurable via the app's `imageOutputType` setting
- Images include unique identifiers for reference in subsequent requests

## Rate Limits

Rate limits depend on your API tier:

- **Gemini API**: Check [Google AI Studio](https://aistudio.google.com/) for current limits
- **Vertex AI**: Based on your Google Cloud project quotas


# Stable Diffusion (https://www.librechat.ai/docs/configuration/tools/stable_diffusion)

Stable Diffusion is a built-in agent tool that connects LibreChat to an **[AUTOMATIC1111 Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)** API. For a dockerized Stable Diffusion deployment, you can also use **[stable-diffusion-webui-docker](https://github.com/AbdBarho/stable-diffusion-webui-docker)**.

With the docker deployment you can skip step 2 and step 3, use the setup instructions from their repository instead.

- Note: you need a compatible GPU ("CPU-only" is possible but very slow). Nvidia is recommended, but there is no clear resource on incompatible GPUs. Any decent GPU should work.

### 1. Follow download and installation instructions

Follow the setup steps from the **[stable-diffusion-webui readme](https://github.com/AUTOMATIC1111/stable-diffusion-webui)**.

### 2. Edit your run script settings

#### Windows

 - Edit your **webui-user.bat** file by adding the following line before the call command:
- `set COMMANDLINE_ARGS=--api`

    - Your .bat file should like this with all other settings default
    ```shell 
    @echo off

    set PYTHON=
    set GIT=
    set VENV_DIR=
    set COMMANDLINE_ARGS=--api

    call webui.bat
    ```
#### Others (not tested but should work)

 - Edit your **webui-user.sh** file by adding the following line:
 - `export COMMANDLINE_ARGS="--api"`

     - Your .sh file should like this with all other settings default
    ```bash 

    export COMMANDLINE_ARGS="--api"

    #!/bin/bash
    #########################################################
    # Uncomment and change the variables below to your need:#
    #########################################################

    # ...rest
    ```

### 3. Run Stable Diffusion (either .sh or .bat file according to your operating system)

### 4. Set the Stable Diffusion URL in LibreChat

> **Note: The default port for Gradio is `7860`. If you changed it, please update the value accordingly.**

#### Docker Install
- Use `SD_WEBUI_URL=http://host.docker.internal:7860` in the `.env` file 
- Or `http://host.docker.internal:7860` from the webui

#### Local Install
- Use `SD_WEBUI_URL=http://127.0.0.1:7860` in the `.env` file 
- Or `http://127.0.0.1:7860` from the webui

Restart LibreChat after changing `.env`.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

#### Add the Tool to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Stable Diffusion**, and save the agent. See the [Agents](/docs/features/agents#tools) section for more information.


# Google Search (https://www.librechat.ai/docs/configuration/tools/google_search)

<Callout type="info" title="Looking for Web Search?">

This page covers the **Google Custom Search** agent tool. For LibreChat's built-in **Web Search** feature (Serper/SearXNG + Firecrawl + Jina), see [Web Search](/docs/features/web_search).

</Callout>

The Google Search tool lets your agents query Google using the Custom Search JSON API. You will need a Google Custom Search Engine ID and an API key.

## Setup

<Steps>
  <Step>

### Create a Programmable Search Engine

Go to the [Programmable Search Engine control panel](https://programmablesearchengine.google.com/controlpanel/all) and sign in with your Google account.

Click **Add** to create a new search engine. Fill in a name, select **Search the entire web**, and click **Create**.

![google_search-2](https://github.com/danny-avila/LibreChat/assets/32828263/152cfe7c-4796-49c6-9160-92cddf38f1c8)

  </Step>
  <Step>

### Copy Your Search Engine ID

After creating the engine, you will see your **Search engine ID**. Copy it -- you will add it to your `.env` file as `GOOGLE_CSE_ID`.

![google_search-4](https://github.com/danny-avila/LibreChat/assets/32828263/e03b5c79-87e5-4a68-b83e-61faf4f2f718)

  </Step>
  <Step>

### Get a Google Search API Key

Go to the [Custom Search JSON API introduction page](https://developers.google.com/custom-search/v1/introduction) and click **Get a Key**.

![google_search-5](https://github.com/danny-avila/LibreChat/assets/32828263/2b93a2f9-5ed2-4794-96a8-a114e346a602)

Name your project, agree to the Terms of Service, and copy the API key.

![google_search-6](https://github.com/danny-avila/LibreChat/assets/32828263/82c9c3ef-7363-40cd-a89e-fc45088e4c86)

  </Step>
  <Step>

### Add Environment Variables

Add both values to your `.env` file:

```bash filename=".env"
GOOGLE_SEARCH_API_KEY=your-api-key-here
GOOGLE_CSE_ID=your-search-engine-id-here
```

  </Step>
  <Step>

### Add the Tool to an Agent

In LibreChat, go to the **Agents** panel and create or edit an agent. In the agent's **Tools** list, select **Google Search**.

  </Step>
  <Step>

### Restart and Test

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop (Ctrl+C) then `npm run backend` |

Send a message like "Search for the latest news about AI" to your agent. The agent will use Google Custom Search to find and return relevant results.

  </Step>
</Steps>

<Callout type="warn" title="Common Issues">

If search returns no results, verify that your Programmable Search Engine is set to **Search the entire web** (not restricted to specific sites). Also confirm that both `GOOGLE_SEARCH_API_KEY` and `GOOGLE_CSE_ID` are set in your `.env` file and that you have restarted LibreChat after making changes.

</Callout>

## Related Pages

<Cards num={3}>
  <Cards.Card title="Web Search" href="/docs/features/web_search" arrow>
    LibreChat's built-in web search feature (Serper, SearXNG, Firecrawl)
  </Cards.Card>
  <Cards.Card title="Tools Overview" href="/docs/configuration/tools" arrow>
    All available agent tools and their configuration
  </Cards.Card>
  <Cards.Card title="Agents" href="/docs/features/agents" arrow>
    Create and configure AI agents with custom tools
  </Cards.Card>
</Cards>


# Tavily Search (https://www.librechat.ai/docs/configuration/tools/tavily)

Tavily Search is a built-in agent tool for current web research. It returns structured search results and can optionally include answers, images, raw page content, domain filters, and recency filters.

## Setup

<Steps>
  <Step>

### Get a Tavily API Key

Create a Tavily account and copy your API key from [app.tavily.com](https://app.tavily.com/).

  </Step>
  <Step>

### Add the Environment Variable

Add the key to your `.env` file:

```bash filename=".env"
TAVILY_API_KEY=tvly-your-api-key
```

  </Step>
  <Step>

### Restart LibreChat

| Deployment | Command                                           |
| ---------- | ------------------------------------------------- |
| Docker     | `docker compose down && docker compose up -d`     |
| Local      | Stop the server, then run `npm run backend` again |

  </Step>
  <Step>

### Add Tavily to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Tavily Search**, and save the agent.

  </Step>
</Steps>

## Parameters

| Parameter                    | Description                                                                                                 |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `query`                      | Search query. Required.                                                                                     |
| `max_results`                | Number of results to return, from 1 to 10. Defaults to 5.                                                   |
| `search_depth`               | `basic` for faster results or `advanced` for higher quality results. Advanced searches count as 2 requests. |
| `include_answer`             | Include Tavily's generated answer in the response.                                                          |
| `include_images`             | Include image results.                                                                                      |
| `include_image_descriptions` | Include descriptions for returned images when images are enabled.                                           |
| `include_raw_content`        | Include raw page content in the result.                                                                     |
| `include_domains`            | Limit results to specific domains.                                                                          |
| `exclude_domains`            | Exclude specific domains.                                                                                   |
| `topic`                      | `general`, `news`, or `finance`.                                                                            |
| `time_range`                 | Limit results to a recent period such as `day`, `week`, `month`, or `year`.                                 |
| `days`                       | Number of days back to include for news searches.                                                           |

## Notes

- Tavily Search is an agent tool. LibreChat can also use Tavily as a [Web Search](/docs/features/web_search) provider or scraper, which is configured separately.
- Tavily requests use the global `PROXY` environment variable when it is configured. When `PROXY` is unset, supported server-side clients honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`/`no_proxy`.
- If the tool does not appear in the Agent Builder, confirm `TAVILY_API_KEY` is set and check [`includedTools`](/docs/configuration/librechat_yaml/object_structure/config#includedtools) or [`filteredTools`](/docs/configuration/librechat_yaml/object_structure/config#filteredtools).


# Traversaal (https://www.librechat.ai/docs/configuration/tools/traversaal)

Traversaal is a built-in agent search tool that sends a natural-language query to Traversaal Ares and returns a response with source URLs when available.

## Setup

<Steps>
  <Step>

### Get a Traversaal API Key

Create an account and get an API key from [api.traversaal.ai](https://api.traversaal.ai/).

  </Step>
  <Step>

### Add the Environment Variable

Add the key to your `.env` file:

```bash filename=".env"
TRAVERSAAL_API_KEY=your-api-key
```

  </Step>
  <Step>

### Restart LibreChat

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

  </Step>
  <Step>

### Add Traversaal to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Traversaal**, and save the agent.

  </Step>
</Steps>

## Parameters

| Parameter | Description |
|-----------|-------------|
| `query` | A complete sentence describing what the agent should search for. Required. |

## Example Prompts

```text
Find recent reporting about open source AI coding agents.
Search for sources comparing renewable energy adoption in Europe and North America.
```

## Troubleshooting

- If Traversaal returns an authentication error, confirm `TRAVERSAAL_API_KEY` is set and restart LibreChat.
- If the tool is not visible in the Agent Builder, check [`includedTools`](/docs/configuration/librechat_yaml/object_structure/config#includedtools), [`filteredTools`](/docs/configuration/librechat_yaml/object_structure/config#filteredtools), and the agent's `tools` capability.


# Azure AI Search (https://www.librechat.ai/docs/configuration/tools/azure_ai_search)

Azure AI Search is a built-in agent tool that lets an agent query your Azure AI Search index and use the returned documents in its answer.

## Configuration

### Required

To get started, you need an Azure AI Search endpoint URL, index name, and API key. Define them in your `.env` file:

```env
AZURE_AI_SEARCH_SERVICE_ENDPOINT="..."
AZURE_AI_SEARCH_INDEX_NAME="..."
AZURE_AI_SEARCH_API_KEY="..."
```

### AZURE_AI_SEARCH_SERVICE_ENDPOINT

This is the URL of the search endpoint. It can be obtained from the top page of the search service in the Cognitive Search management console (e.g., `https://example.search.windows.net`).

### AZURE_AI_SEARCH_INDEX_NAME

This is the name of the index to be searched (e.g., `hotels-sample-index`).

### AZURE_AI_SEARCH_API_KEY

This is the authentication key to use when utilizing the search endpoint. Please issue it from the management console. Use the Value, not the name of the authentication key.

# Introduction to tutorial

## Create or log in to your account on Azure Portal

**1.** Visit **[https://azure.microsoft.com/en-us/](https://azure.microsoft.com/en-us/)** and click on `Get started` or `Try Azure for Free` to create an account and sign in.

**2.** Choose pay per use or Azure Free with $200.

![image](/images/azure-ai-search/azure_portal_welcome.png)

## Create the Azure AI Search service

**1.** Access your control panel.

**2.** Click on `Create a resource`.

![image](/images/azure-ai-search/azure_portal_menu.png)

**3.** Search for `Azure Search` in the bar and press enter.

![image](/images/azure-ai-search/azure_ai_search_in_marketplace.png)

**4.** Now, click on `Create`.

**5.** Configure the basics settings, create a new or select an existing Resource Group, name the Service Name with a name of your preference, and then select the location.

![image](/images/azure-ai-search/create_azure_ai_search.png)

**6.** Click on `Change Pricing Tier`.

![image](/images/azure-ai-search/azure_ai_tier.png)

Now select the free option or select your preferred option (may incur charges).

![image](/images/azure-ai-search/azure_ai_free_tier.png)

**7.** Click on `Review + create` and wait for the resource to be created.

![image](/images/azure-ai-search/create_azure_ai_search_button.png)

## Create your index

**1.** Click on `Import data`.

![image](/images/azure-ai-search/azure_ai_search_instance_info.png)

**2.** Follow the Microsoft tutorial: **[https://learn.microsoft.com/en-us/azure/search/search-get-started-portal](https://learn.microsoft.com/en-us/azure/search/search-get-started-portal)**, after finishing, save the name given to the index somewhere.

**3.** Now you have your `AZURE_AI_SEARCH_INDEX_NAME`, copy and save it in a local safe place.

## Get the Endpoint

**1.** In the `Url:` you have your `AZURE_AI_SEARCH_SERVICE_ENDPOINT`, copy and save it in a local safe place.

![image](/images/azure-ai-search/azure_ai_search_instance_info.png)

**2.** On the left panel, click on `keys`.

![image](/images/azure-ai-search/azure_ai_search_menu.png)

**3.** Click on `Add` and insert a name for your key.

**4.** Copy the key to get `AZURE_AI_SEARCH_API_KEY`.

![image](/images/azure-ai-search/azure_ai_search_keys.png)

## Add the Tool to an Agent

After adding the environment variables, restart LibreChat and add **Azure AI Search** to an agent.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Azure AI Search**, and save the agent.

## Test It

Ask the agent a question that should be answered by your Azure AI Search index. If the tool returns too much content, tune `AZURE_AI_SEARCH_SEARCH_OPTION_TOP` and `AZURE_AI_SEARCH_SEARCH_OPTION_SELECT`.

![image](/images/azure-ai-search/chat_with_azure_ai_search.png)

## Optional

The following are configuration values that are not required but can be specified as parameters during a search.

If there are concerns that the search result data may be too large and exceed the prompt size, consider reducing the size of the search result data by using AZURE_AI_SEARCH_SEARCH_OPTION_TOP and AZURE_AI_SEARCH_SEARCH_OPTION_SELECT.

For details on each parameter, please refer to the following document:
**[https://learn.microsoft.com/en-us/rest/api/searchservice/search-documents](https://learn.microsoft.com/en-us/rest/api/searchservice/search-documents)**

```env
AZURE_AI_SEARCH_API_VERSION=2023-10-01-Preview
AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE=simple
AZURE_AI_SEARCH_SEARCH_OPTION_TOP=3
AZURE_AI_SEARCH_SEARCH_OPTION_SELECT=field1, field2, field3
```

#### AZURE_AI_SEARCH_API_VERSION

Specify the version of the search API. When using new features such as semantic search or vector search, you may need to specify the preview version. The default value is `2023-11-1`.

#### AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE

Specify `simple` or `full`. The default value is `simple`.

#### AZURE_AI_SEARCH_SEARCH_OPTION_TOP

Specify the number of items to search for. The default value is 5.

#### AZURE_AI_SEARCH_SEARCH_OPTION_SELECT

Specify the fields of the index to be retrieved, separated by commas. Please note that these are not the fields to be searched.


# OpenWeather (https://www.librechat.ai/docs/configuration/tools/openweather)

The OpenWeather tool lets agents get weather data including current conditions, forecasts, historical data, and daily summaries using OpenWeather's One Call API 3.0.

## Prerequisites

- An OpenWeather account
- An OpenWeather API key (specifically for the One Call API 3.0)

## Getting an API Key

1. Sign up for an OpenWeather account at [OpenWeather](https://home.openweathermap.org/users/sign_up)
2. After signing in, go to your [API keys](https://home.openweathermap.org/api_keys) page
3. Generate a new API key if you don't have one
4. Subscribe to the [One Call API 3.0](https://openweathermap.org/api/one-call-3) plan
5. Wait for your API key to be activated (can take up to 2 hours)

## Configuration

### Environment Variables

Add the following to your `.env` file:

```bash
OPENWEATHER_API_KEY=your_api_key_here
```

### Add the Tool to an Agent

Restart LibreChat after changing `.env`, then add **OpenWeather** to any [agent](/docs/features/agents).

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **OpenWeather**, and save the agent.

## Usage

The OpenWeather tool supports the following actions:

- `current_forecast`: Get current weather and forecast data
- `timestamp`: Get historical weather data for a specific date
- `daily_aggregation`: Get aggregated weather data for a specific date
- `overview`: Get a human-readable weather summary

### Example Prompts

```
What's the current weather in London?
What was the weather like in Paris on 2023-01-01?
Give me a weather summary for Tokyo.
What's the temperature in New York in Fahrenheit?
```

### Parameters

- `city`: Name of the city (if lat/lon not provided)
- `lat`: Latitude coordinate (optional if city provided)
- `lon`: Longitude coordinate (optional if city provided)
- `units`: Temperature units ("Celsius", "Kelvin", or "Fahrenheit")
- `lang`: Language code for weather descriptions (e.g., "en", "fr", "es")
- `date`: Date in YYYY-MM-DD format (required for timestamp and daily_aggregation actions)
- `tz`: Timezone (optional, for daily_aggregation action)

## Troubleshooting

Common issues and solutions:

1. **403 Unauthorized Error**
   - Verify your API key is correct
   - Check if your API key has been activated (wait 2 hours after creation)
   - Ensure you have subscribed to the One Call API 3.0

2. **City Not Found**
   - Check the spelling of the city name
   - Try adding the country code (e.g., "London,UK")
   - Use latitude and longitude coordinates instead

3. **Invalid Date Format**
   - Ensure dates are in YYYY-MM-DD format
   - Historical data is only available from 1979-01-01
   - Future data is limited to 1.5 years ahead

## API Limits

- Check your [OpenWeather subscription](https://home.openweathermap.org/subscriptions) for your specific limits
- Consider implementing rate limiting in high-traffic environments

## Support

For issues with the tool:
- You may open an issue at https://github.com/jmaddington/LibreChat/issues or 
- Check the [LibreChat Issues](https://github.com/danny-avila/LibreChat/issues)
- Review OpenWeather's [API documentation](https://openweathermap.org/api/one-call-3)
- Contact OpenWeather [support](https://openweathermap.org/support-centre) for API-specific issues

## Notes

- Temperature values are automatically rounded to the nearest degree
- Default temperature unit is Celsius if not specified


# Wolfram|Alpha (https://www.librechat.ai/docs/configuration/tools/wolfram)

The Wolfram|Alpha tool gives agents access to computation, math, curated knowledge, unit conversion, scientific data, and real-time data. An AppID must be supplied in all calls to the Wolfram|Alpha API.

- Note: Wolfram API calls are limited to 100 calls/day and 2000/month for regular users.

### Make an account 
- Visit: **[products.wolframalpha.com/api/](https://products.wolframalpha.com/api/)** to create your account

### Get your AppID
- Visit the **[Developer Portal](https://developer.wolframalpha.com/access)** and click on `Get an AppID`
- Select `LLM API` as the `API` and copy the key

### Configure LibreChat

Add your AppID to `.env`:

```bash filename=".env"
WOLFRAM_APP_ID=your-app-id
```

Restart LibreChat after changing `.env`.

| Deployment | Command |
|------------|---------|
| Docker | `docker compose down && docker compose up -d` |
| Local | Stop the server, then run `npm run backend` again |

#### Add the Tool to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Wolfram**, and save the agent. See the [Agents](/docs/features/agents#tools) section for more information.


# Calculator (https://www.librechat.ai/docs/configuration/tools/calculator)

Calculator is a built-in agent tool for arithmetic and symbolic calculations. It does not require an API key or environment variable.

## Setup

<Steps>
  <Step>

### Add Calculator to an Agent

In LibreChat, select **Agents**, create or edit an agent, open the agent's **Tools** list, select **Calculator**, and save the agent.

  </Step>
  <Step>

### Test It

Ask the agent to calculate something that benefits from a tool call:

```text
Calculate 12345 * 6789 and show the result.
```

  </Step>
</Steps>


# Speech Settings (https://www.librechat.ai/docs/configuration/stt_tts)

<Callout type="info" title="Upcoming STT/TTS Enhancements" collapsible>
The Google Cloud STT/TTS and Deepgram services are being planned for future integration.
</Callout>

## Speech Introduction

The Speech Configuration includes settings for both Speech-to-Text (STT) and Text-to-Speech (TTS) under a unified `speech:` section. Additionally, there is a new `speechTab` menu for user-specific settings.

> **See Also:** For detailed YAML configuration schema and all available options, see the [Speech Object Structure](/docs/configuration/librechat_yaml/object_structure/speech) documentation.

### Environment Variables

When using cloud-based STT/TTS services, you'll need to set API keys in your `.env` file:

```bash filename=".env"
# Speech-to-Text API key (e.g., OpenAI Whisper)
STT_API_KEY=your-stt-api-key

# Text-to-Speech API key (e.g., OpenAI TTS, ElevenLabs)
TTS_API_KEY=your-tts-api-key
```

These keys are then referenced in your `librechat.yaml` configuration using `${STT_API_KEY}` and `${TTS_API_KEY}`.

### Self-hosted engines need an allowedAddresses entry

Both `speech.stt` and `speech.tts` accept an `allowedAddresses` list. Outbound speech requests are validated against their resolved IP and blocked from reaching private, loopback, and link-local address space, so a self-hosted engine on `localhost`, a LAN address, or a Docker service name is unreachable until you list it:

```yaml filename="librechat.yaml"
speech:
  tts:
    allowedAddresses:
      - 'host.docker.internal:8080'
    localai:
      url: 'http://host.docker.internal:8080/tts'
      # ...
```

Entries are bare `host:port` pairs: no scheme or path, port required, IPv6 bracketed as `[::1]:8080`, and IP literals must be private. Public cloud endpoints such as OpenAI, Azure, and ElevenLabs need no entry. The guard works on the resolved IP, not the hostname, so a cloud endpoint reached over Private Link, private DNS, or a VPN resolves into private address space and does need its exact `host:port` listed like any other private target. The same field and rules apply under `speech.stt`. See [SSRF protection](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers) for the full entry format.

## Speech Tab (optional)

The `speechTab` menu provides customizable options for conversation and advanced modes, as well as detailed settings for STT and TTS. This will set the default settings for users

Use `browser` for built-in browser speech or `external` for a server-side provider configured below. Older provider-specific defaults remain accepted for compatibility and are normalized to `external`; if the matching external service is unavailable, LibreChat falls back to `browser`.

example:

```yaml
speech:
  speechTab:
    conversationMode: true
    advancedMode: false
    speechToText:
      engineSTT: "external"
      languageSTT: "English (US)"
      autoTranscribeAudio: true
      decibelValue: -45
      autoSendText: 0
    textToSpeech:
      engineTTS: "external"
      voice: "alloy"
      languageTTS: "en"
      automaticPlayback: true
      playbackRate: 1.0
      cacheTTS: true
```

`speechTab` sets the initial values users see; each remains changeable per user in the speech settings tab.

**Top-level keys:**

<OptionTable
  options={[
    ['conversationMode', 'Boolean', 'Starts the speech tab in conversation mode, which chains transcription and playback for hands-free back-and-forth.', ''],
    ['advancedMode', 'Boolean', 'Reveals the advanced speech settings in the UI instead of only the basic switches.', ''],
    ['speechToText', 'Boolean or Object', 'Set `false` to turn STT off, `true` to enable it with app defaults, or an object to preset the fields below.', ''],
    ['textToSpeech', 'Boolean or Object', 'Set `false` to turn TTS off, `true` to enable it with app defaults, or an object to preset the fields below.', ''],
  ]}
/>

**`speechToText` subkeys:**

<OptionTable
  options={[
    ['engineSTT', 'String', 'Which transcription engine to use. `browser` uses the built-in Web Speech API and needs no server config; the others use the matching provider block under `speech.stt`.', 'Options: "browser", "external", "openai", "azureOpenAI"'],
    ['languageSTT', 'String', 'Language the transcriber should expect, as shown in the speech settings dropdown.', 'Example: "English (US)"'],
    ['autoTranscribeAudio', 'Boolean', 'Keep the microphone listening instead of stopping at the first pause. With an external engine it also turns on silence detection, which uses `decibelValue` to decide when you have stopped speaking and ends the recording. You still start the recording yourself.', ''],
    ['decibelValue', 'Number', 'Silence threshold in dB used by that silence detection. Lower values are more sensitive to quiet speech.', 'Range: -100 to -30. Default: -45'],
    ['autoSendText', 'Number', 'Seconds to wait after transcription finishes before sending the message automatically. `0` sends immediately; `-1` disables auto-send.', 'Range: 0 to 60, or -1'],
  ]}
/>

**`textToSpeech` subkeys:**

<OptionTable
  options={[
    ['engineTTS', 'String', 'Which speech engine to use. `browser` uses the built-in Web Speech API and needs no server config; the others use the matching provider block under `speech.tts`.', 'Options: "browser", "external", "openai", "azureOpenAI", "elevenlabs", "localai"'],
    ['voice', 'String', 'Default voice name. For external engines it must be one of the voices listed for that engine under `speech.tts`. The `browser` engine uses whatever voices the browser and operating system provide, so it needs no server-side list.', 'Example: "alloy"'],
    ['languageTTS', 'String', 'Language used by the browser engine.', 'Example: "en"'],
    ['automaticPlayback', 'Boolean', 'Play each response aloud as soon as it finishes generating.', ''],
    ['playbackRate', 'Number', 'Playback speed multiplier.', 'Range: 0.25 to 4'],
    ['cacheTTS', 'Boolean', 'Reuse previously generated audio for the same text instead of re-requesting it from the provider.', ''],
  ]}
/>

<Callout type="info" title="engine values and the provider blocks">

`engineSTT` / `engineTTS` only choose which engine the UI starts on. Anything other than `browser` still needs the corresponding provider configured under `speech.stt` or `speech.tts`. See the sections below.

</Callout>

## STT (Speech-to-Text)

The Speech-to-Text (STT) feature converts spoken words into written text. To enable STT, click on the STT button (near the send button) or use the key combination ++Ctrl+Alt+L++ to start the transcription.

### Available STT Services

- **Local STT**
  - Browser-based
  - Whisper (tested on LocalAI)
- **Cloud STT**
  - OpenAI Whisper
  - Azure Whisper
  - Other OpenAI-compatible STT services

### Configuring Local STT

- #### Browser-based
  No setup required. Ensure the "Speech To Text" switch in the speech settings tab is enabled and "Browser" is selected in the engine dropdown.

- #### Whisper Local
  Requires a local Whisper instance.

```yaml
speech:
  stt:
    openai:
      url: 'http://host.docker.internal:8080/v1/audio/transcriptions'
      model: 'whisper'
```

### Configuring Cloud STT

- #### OpenAI Whisper

```yaml
speech:
  stt:
    openai:
      apiKey: '${STT_API_KEY}'
      model: 'whisper-1'
```

- #### Azure Whisper

```yaml
speech:
  stt:
    azureOpenAI:
      instanceName: 'instanceName'
      apiKey: '${STT_API_KEY}'
      deploymentName: 'deploymentName'
      apiVersion: 'apiVersion'
```

<Callout type="info" title="Azure Endpoint Domain Support">
The `instanceName` field supports both Azure OpenAI domain formats:
- **New format**: `.cognitiveservices.azure.com` (e.g., `my-instance.cognitiveservices.azure.com`)
- **Legacy format**: `.openai.azure.com` (e.g., `my-instance.openai.azure.com`)

You can specify either the full domain or just the instance name. If you provide a full domain including `.azure.com`, it will be used as-is. Otherwise, the legacy `.openai.azure.com` format will be applied for backward compatibility.
</Callout>

- #### OpenAI compatible

Refer to the OpenAI Whisper section, adjusting the `url` and `model` as needed.

example
  
```yaml
speech:
  stt:
    openai:
      url: 'http://host.docker.internal:8080/v1/audio/transcriptions'
      model: 'whisper'
  ```


## TTS (Text-to-Speech)

The Text-to-Speech (TTS) feature converts written text into spoken words. Various TTS services are available:

### Available TTS Services

- **Local TTS**
  - Browser-based
  - Piper (tested on LocalAI)
  - Coqui (tested on LocalAI)
- **Cloud TTS**
  - OpenAI TTS
  - Azure OpenAI
  - ElevenLabs
  - Other OpenAI/ElevenLabs-compatible TTS services

### Configuring Local TTS

- #### Browser-based

No setup required. Ensure the "Text To Speech" switch in the speech settings tab is enabled and "Browser" is selected in the engine dropdown.

- #### Piper

Requires a local Piper instance.

```yaml
speech:
  tts:
    localai:
      url: "http://host.docker.internal:8080/tts"
      apiKey: "EMPTY"
      voices: [
        "en-us-amy-low.onnx",
        "en-us-danny-low.onnx",
        "en-us-libritts-high.onnx",
        "en-us-ryan-high.onnx",
      ]
      backend: "piper"
```

- #### Coqui

Requires a local Coqui instance.

```yaml
speech:
  tts:
    localai:
      url: 'http://localhost:8080/v1/audio/synthesize'
      voices: ['tts_models/en/ljspeech/glow-tts', 'tts_models/en/ljspeech/tacotron2', 'tts_models/en/ljspeech/waveglow']
      backend: 'coqui'
```

### Configuring Cloud TTS

- #### OpenAI TTS

```yaml
speech:
  tts:
    openai:
      apiKey: '${TTS_API_KEY}'
      model: 'tts-1'
      voices: ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']
```

- #### Azure OpenAI

```yaml
speech:
  tts:
    azureOpenAI:
      instanceName: ''
      apiKey: '${TTS_API_KEY}'
      deploymentName: ''
      apiVersion: ''
      model: 'tts-1'
      voices: ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']
```

<Callout type="info" title="Azure Endpoint Domain Support">
The `instanceName` field supports both Azure OpenAI domain formats:
- **New format**: `.cognitiveservices.azure.com` (e.g., `my-instance.cognitiveservices.azure.com`)
- **Legacy format**: `.openai.azure.com` (e.g., `my-instance.openai.azure.com`)

You can specify either the full domain or just the instance name. If you provide a full domain including `.azure.com`, it will be used as-is. Otherwise, the legacy `.openai.azure.com` format will be applied for backward compatibility.
</Callout>

- #### ElevenLabs

```yaml
speech:
  tts:
    elevenlabs:
      apiKey: '${TTS_API_KEY}'
      model: 'eleven_multilingual_v2'
      voices: ['202898wioas09d2', 'addwqr324tesfsf', '3asdasr3qrq44w', 'adsadsa']
```

Additional ElevenLabs-specific parameters can be added as follows:

```yaml
      voice_settings:
        similarity_boost: '' # number
        stability: '' # number
        style: '' # number
        use_speaker_boost: # boolean
      pronunciation_dictionary_locators: [''] # list of strings (array)
```

- #### OpenAI compatible

Refer to the OpenAI TTS section, adjusting the `url` variable as needed

example:

```yaml
speech:
  tts:
    openai:
      url: 'http://host.docker.internal:8080/v1/audio/synthesize'
      apiKey: '${TTS_API_KEY}'
      model: 'tts-1'
      voices: ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']
```

- #### ElevenLabs compatible

Refer to the ElevenLabs section, adjusting the `url` variable as needed

example:

```yaml
speech:
  tts:
    elevenlabs:
      url: 'http://host.docker.internal:8080/v1/audio/synthesize'
      apiKey: '${TTS_API_KEY}'
      model: 'eleven_multilingual_v2'
      voices: ['202898wioas09d2', 'addwqr324tesfsf', '3asdasr3qrq44w', 'adsadsa']
```


# MongoDB Atlas (https://www.librechat.ai/docs/configuration/mongodb/mongodb_atlas)

<Steps>
<Step>

### Create a MongoDB Atlas Account

1. Open a new tab in your web browser and go to [account.mongodb.com/account/register](https://account.mongodb.com/account/register).
2. Fill out the required information and create your account.

</Step>
<Step>

### Create a New Project

After setting up your account, click on the "New Project" button and give it a name (e.g., "LibreChat").

</Step>
<Step>

### Build a Database

Click on the "Build a Database" button.

</Step>
<Step>

### Choose the Free Tier

Select the "Shared Clusters" option, which is the free tier.

</Step>
<Step>

### Name Your Cluster

Give your cluster a name (e.g., "LibreChat-Cluster") and click "Create Cluster".

</Step>
<Step>

### Set Up Database Credentials

1. Click on the "Database & Network Access" option in the sidebar.
2. Click on the "Add New Database User" button.
3. Enter a username and a secure password, then click "Add User".

</Step>
<Step>

### Configure Network Access

1. Click on the "IP Access List" option in the sidebar.
2. Click on the "Add IP Address" button.
3. Enter "0.0.0.0/0" and click "Confirm".

</Step>
<Step>

### Get Your Connection String

1. Click on the "Project Overview" option in the sidebar.
2. In "Application Development" click "Get connection string".
3. Click on the "Connect" button.
4. Select "Connect Your Application".
5. Copy the connection string provided.
6. Replace `<db_username>` and `<db_password>` in the connection string with the username password you set in the credentials step. Remove the `<>` characters around the password.

Your final connection string should look something like this:

```sh filename="Connection String"
mongodb+srv://username:password@cluster-url.mongodb.net/LibreChat?retryWrites=true
```

</Step>
<Step>

### Update the .env File

1. In your LibreChat project, open the `.env` file.
2. Find the `MONGO_URI` variable and paste your connection string:

```sh filename=".env"
MONGO_URI=mongodb+srv://username:password@cluster-url.mongodb.net/LibreChat?retryWrites=true
```

</Step>
</Steps>

That's it! You've now set up an online MongoDB database for LibreChat using MongoDB Atlas, and you've updated your LibreChat application to use this database connection. Your application should now be able to connect to the online MongoDB database.

## Note about Docker

<Callout type="note" title="Docker">
**Note:** If you're using LibreChat with Docker, you'll need to utilize the `docker-compose.override.yml` file. This override file allows you to prevent the installation of the included MongoDB instance. Instead, your LibreChat Docker container will use the online MongoDB Atlas database you've just set up. For more information on using the override file, please refer to our [Docker Override Guide](/docs/configuration/docker_override).
</Callout>



# MongoDB Authentication (https://www.librechat.ai/docs/configuration/mongodb/mongodb_auth)

This guide will demonstrate how to use the `docker-compose.override.yml` file to allows us to enable explicit authentication for MongoDB.

For more info about the override file, please consult: [Docker Compose Override](/docs/configuration/docker_override)

**Notes:**

- The default configuration is secure by blocking external port access, but we can take it a step further with access credentials.
- As noted by the developers of MongoDB themselves, authentication in MongoDB is fairly complex. We will be taking a simple approach that will be good enough for most cases, especially for existing configurations of LibreChat. To learn more about how mongodb authentication works with docker, see here: https://hub.docker.com/_/mongo/
- This guide focuses exclusively on terminal-based setup procedures.
- While the steps outlined may also be applicable to Docker Desktop environments, or with non-Docker, local MongoDB, or other container setups, details specific to those scenarios are not provided.

**There are 3 basic steps:**

- Create an admin user within your mongodb container
- Enable authentication and create a "readWrite" user for "LibreChat"
- Configure the MONGO_URI with newly created user

## TL;DR

These are all the necessary commands if you'd like to run through these quickly or for reference:

<Callout type="abstract" title="TL;DR - All Commands" emoji='💻' collapsible>

```bash filename="Shut down container initially"
docker compose down
```

```bash filename="Start MongoDB container"
docker compose up -d mongodb
```

```bash filename="Open MongoDB shell on 'chat-mongodb' container"
docker exec -it chat-mongodb mongosh
```

```bash filename="Switch to admin database"
use admin
```

```bash filename="Create new admin user"
db.createUser({ user: "adminUser", pwd: "securePassword", roles: ["userAdminAnyDatabase", "readWriteAnyDatabase"] })
```

```bash filename="Exit MongoDB shell"
exit
```

```bash filename="Shut down container after setup"
docker compose down
```

```bash filename="Restart MongoDB container with authentication"
docker compose up -d mongodb
```

```bash filename="Log into MongoDB shell with credentials"
docker exec -it chat-mongodb mongosh -u adminUser -p securePassword --authenticationDatabase admin
```

```bash filename="Switch to LibreChat database"
use LibreChat
```

```bash filename="Create user in LibreChat database"
db.createUser({ user: 'user', pwd: 'userpasswd', roles: [ { role: "readWrite", db: "LibreChat" } ] });
```

```bash filename="Exit MongoDB shell after creating user"
exit
```

```bash filename="Shut down container after user creation"
docker compose down
```

```bash filename="Start all services with final settings"
docker compose up
```
</Callout>

### Example

Example `docker-compose.override.yml` file using the [`librechat.yaml` config file](/docs/configuration/librechat_yaml), [MongoDB Authentication](/docs/configuration/mongodb/mongodb_auth), and `mongo-express` for [managing your MongoDB database](/blog/2023-11-30_mongoexpress):

<Callout type="example" title="Example `docker-compose.override.yml` file" collapsible>

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  api:
    volumes:
      - ./librechat.yaml:/app/librechat.yaml
    environment:
      - MONGO_URI=mongodb://user:userpasswd@mongodb:27017/LibreChat
  mongodb:
    command: mongod --auth
  mongo-express:
    image: mongo-express
    container_name: mongo-express
    environment:
      ME_CONFIG_MONGODB_SERVER: mongodb
      ME_CONFIG_BASICAUTH_USERNAME: admin
      ME_CONFIG_BASICAUTH_PASSWORD: password
      ME_CONFIG_MONGODB_URL: 'mongodb://adminUser:securePassword@mongodb:27017'
      ME_CONFIG_MONGODB_ADMINUSERNAME: adminUser
      ME_CONFIG_MONGODB_ADMINPASSWORD: securePassword
    ports:
      - '8081:8081'
    depends_on:
      - mongodb
    restart: always
```
</Callout>

<Steps>
<Step>

### Creating an Admin User

First, we must stop the default containers from running, and only run the mongodb container.

```bash filename="Stop all running containers"
docker compose down
```

```bash filename="Start mongodb container in detached mode"
docker compose up -d mongodb
```

> Note: The `-d` flag detaches the current terminal instance as the container runs in the background. If you would like to see the mongodb log outputs, omit it and continue in a separate terminal.

Once running, we will enter the container's terminal and execute `mongosh`:

```bash filename="Connect to the MongoDB shell"
docker exec -it chat-mongodb mongosh
```
You should see the following output:

```bash filename="Output"
~/LibreChat$ docker exec -it chat-mongodb mongosh
Current Mongosh Log ID: 65bfed36f7d7e3c2b01bcc3d
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.1
Using MongoDB:          7.0.4
Using Mongosh:          2.1.1

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

test> 
```

Optional: While we're here, we can disable telemetry for mongodb if desired, which is anonymous usage data collected and sent to MongoDB periodically:

Execute the command below.

> Notes:
> - All subsequent commands should be run in the current terminal session, regardless of the environment (Docker, Linux, `mongosh`, etc.)
> - I will represent the actual terminal view with # example input/output or simply showing the output in some cases

Command:

```bash filename="Disable Telemetry"
disableTelemetry()
```

Example input/output:

```bash filename="example input/output"
test> disableTelemetry()
Telemetry is now disabled.
```

Now, we must access the admin database, which mongodb creates by default to create our admin user:

```bash filename="Switch to Admin Database"
use admin
```

> switched to db admin

Replace the credentials as desired and keep in your secure records for the rest of the guide.

Run command to create the admin user:

```bash filename="Create Admin User"
db.createUser({ user: "adminUser", pwd: "securePassword", roles: ["userAdminAnyDatabase", "readWriteAnyDatabase"] })
```

You should see an "ok" output.

You can also confirm the admin was created by running `show users`:

```bash filename="example input/output"
admin> show users
[
  {
    _id: 'admin.adminUser',
    userId: UUID('86e90441-b5b7-4043-9662-305540dfa6cf'),
    user: 'adminUser',
    db: 'admin',
    roles: [
      { role: 'userAdminAnyDatabase', db: 'admin' },
      { role: 'readWriteAnyDatabase', db: 'admin' }
    ],
    mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
  }
]
```

:warning: **Important:** if you are using `mongo-express` to manage your database, you need the additional permissions for the `mongo-express` service to run correctly:

```bash filename="Grant Roles to Admin User"
db.grantRolesToUser("adminUser", ["clusterAdmin", "readAnyDatabase"])
```

Exit the Mongosh/Container Terminal by running `exit`:
```bash filename="Exit the Mongosh/Container Terminal"
admin> exit
```

And shut down the running container:
```bash filename="Shut down the running container"
docker compose down
```

</Step>
<Step>

### Enabling Authentication and Creating a User with `readWrite` Access

We must now create/edit the `docker-compose.override.yml` file to enable authentication for our mongodb container. You can use this configuration to start or reference:

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  api:
    volumes:
      - ./librechat.yaml:/app/librechat.yaml # Optional for using the librechat config file.
  mongodb:
    command: mongod --auth # <--- Add this to enable authentication
```

After configuring the override file as above, run the mongodb container again:

```bash filename="Start the MongoDB container"
docker compose up -d mongodb
```

And access mongosh as the admin user:

```bash filename="Connect to MongoDB container using mongo shell"
docker exec -it chat-mongodb mongosh -u adminUser -p securePassword --authenticationDatabase admin
```

Confirm you are authenticated:
```bash filename="Check MongoDB Connection Status Command"
db.runCommand({ connectionStatus: 1 })
```

```bash filename="example input/output"
test> db.runCommand({ connectionStatus: 1 })
{
  authInfo: {
    authenticatedUsers: [ { user: 'adminUser', db: 'admin' } ],
    authenticatedUserRoles: [
      { role: 'readWriteAnyDatabase', db: 'admin' },
      { role: 'userAdminAnyDatabase', db: 'admin' }
    ]
  },
  ok: 1
}
test>
```

Switch to the "LibreChat" database

> Note: This the default database unless you changed it via the MONGO_URI; default URI: `MONGO_URI=mongodb://mongodb:27017/LibreChat`

```bash filename="Switch to the LibreChat database"
use LibreChat
```

Now we'll create the actual credentials to be used by our Mongo connection string, which will be limited to read/write access of the "LibreChat" database. As before, replace the example with your desired credentials:

`db.createUser({ user: 'user', pwd: 'userpasswd', roles: [ { role: "readWrite", db: "LibreChat" } ] });`

You should see an "ok" output again.

You can verify the user creation with the `show users` command.

Exit the Mongosh/Container Terminal again with `exit`, and bring the container down:

```bash filename="End the current shell session"
exit
```

```bash filename="Stop the current Docker Compose services"
docker compose down
```

I had an issue where the newly created user would not persist after creating it. To solve this, I simply repeated the steps to ensure it was created. Here they are for your convenience:

```bash filename="Shut down container"
docker compose down
```

```bash filename="Start Mongo container"
docker compose up -d mongodb
```

```bash filename="Access MongoDB shell as admin"
docker exec -it chat-mongodb mongosh -u adminUser -p securePassword --authenticationDatabase admin
```

```bash filename="Switch to LibreChat database"
use LibreChat
```

```bash filename="Show current users in LibreChat database"
show users
```

```bash filename="Create a new user in LibreChat database"
db.createUser({ user: 'user', pwd: 'userpasswd', roles: [ { role: "readWrite", db: "LibreChat" } ] });
```

If it's still not persisting, you can try running the commands with all containers running, but note that the `LibreChat` container will be in an error/retrying state.

</Step>
<Step>

### Update the `MONGO_URI` to Use the New Credentials

Finally, we add the new connection string with our newly created credentials to our `docker-compose.override.yml` file under the `api` service:

```yaml filename="docker-compose.override.yml"
    environment:
      - MONGO_URI=mongodb://user:userpasswd@mongodb:27017/LibreChat
```

So our override file looks like this now:

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  api:
    volumes:
      - ./librechat.yaml:/app/librechat.yaml
    environment:
      - MONGO_URI=mongodb://user:userpasswd@mongodb:27017/LibreChat
  mongodb:
    command: mongod --auth
```

You should now run `docker compose up` successfully authenticated with read/write access to the LibreChat database

Example successful connection:
```bash filename="successful connection example"
LibreChat         | 2024-02-04 20:59:43 info: Server listening on all interfaces at port 3080. Use http://localhost:3080 to access it
chat-mongodb      | {"t":{"$date":"2024-02-04T20:59:53.880+00:00"},"s":"I",  "c":"NETWORK",  "id":22943,   "ctx":"listener","msg":"Connection accepted","attr":{"remote":"192.168.160.4:58114","uuid":{"uuid":{"$uuid":"027bdc7b-a3f4-429a-80ee-36cd172058ec"}},"connectionId":17,"connectionCount":10}}
```

If you're having Authentication errors, run the last part of Step 2 again. I'm not sure why it's finicky but it will work after a few tries.

</Step>
</Steps>



# MongoDB Community Server (https://www.librechat.ai/docs/configuration/mongodb/mongodb_community)

<Steps>
<Step>

### Download MongoDB Community Server

- Go to the official MongoDB website: [https://www.mongodb.com/try/download/community](https://www.mongodb.com/try/download/community)
- Select your operating system and download the appropriate package.

</Step>
<Step>

### Install MongoDB Community Server

Follow the installation instructions for your operating system to install MongoDB Community Server.

</Step>
<Step>

### Create a Data Directory

MongoDB requires a data directory to store its data files. Create a directory on your system where you want to store the MongoDB data files (e.g., `/path/to/data/directory`).

</Step>
<Step>

### Start the MongoDB Server

- Open a terminal or command prompt.
- Navigate to the MongoDB installation directory (e.g., `/path/to/mongodb/bin`).
- Run the following command to start the MongoDB server, replacing `/path/to/data/directory` with the path to the data directory you created in the previous step:

```sh filename="Start the MongoDB Server"
./mongod --dbpath=/path/to/data/directory
```

</Step>
<Step>

### Configure MongoDB for Remote Access (Optional)

If you plan to access the MongoDB server from a remote location (e.g., a different machine or a LibreChat instance hosted elsewhere), you need to configure MongoDB for remote access:

- Create a configuration file (e.g., `/path/to/mongodb/config/mongodb.conf`) with the following content:

```yaml filename="mongodb.conf"
# Network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0
```
- Stop the MongoDB server if it's running.
- Start the MongoDB server with the configuration file:

```yaml filename="Start the MongoDB server"
./mongod --config /path/to/mongodb/config/mongodb.conf
```

</Step>
<Step>

### Get the Connection String

The connection string for your MongoDB Community Server will be in the following format:

```sh filename="Connection String"
mongodb://[hostname]:[port]
```
Replace `[hostname]` with the IP address or hostname of the machine where MongoDB is running, and `[port]` with the port number (usually 27017).

</Step>
<Step>

### Update the .env File

- In your LibreChat project, open the `.env` file.
- Find the `MONGO_URI` variable and paste your connection string:

```sh filename=".env"
MONGO_URI=mongodb://[hostname]:[port]
```

</Step>
</Steps>

That's it! You've now set up a MongoDB Community Server for LibreChat. Your LibreChat application should be able to connect to the local MongoDB instance using the connection string you provided.

## Note about Docker

<Callout type="note" title="Docker">
**Note:** If you're using LibreChat with Docker, you'll need to utilize the `docker-compose.override.yml` file. This override file allows you to prevent the installation of the included MongoDB instance. Instead, your LibreChat Docker container will use the local MongoDB Community Server database you've just set up. For more information on using the override file, please refer to our [Docker Override Guide](/docs/configuration/docker_override).

**Example:**
```yaml filename="docker-compose.override.yml"
services:
  api:
    environment:
    - MONGO_URI=mongodb://user:pass@host1:27017,host2:27017,host3:27017/LibreChat?authSource=admin&replicaSet=setname
```
</Callout>


# Redis (https://www.librechat.ai/docs/configuration/redis)

This guide covers how to configure Redis for caching and session storage in LibreChat. Redis provides significant performance improvements and is **required for horizontal scaling**—if you're running multiple LibreChat instances behind a load balancer, Redis ensures consistent state across all instances.

## Table of Contents

- [Basic Setup](#basic-setup)
- [Connection Types](#connection-types)
- [Security Configuration](#security-configuration)
- [Advanced Options](#advanced-options)
- [Replica Failover Recovery](#replica-failover-recovery)
- [Performance Tuning](#performance-tuning)
- [Configuration Examples](#configuration-examples)
- [Resumable Streams](#resumable-streams)
- [Generation Protocol Compatibility](#generation-protocol-compatibility)
- [Stream Delta Coalescing](#stream-delta-coalescing)
- [MCP Catalog Coordination](#mcp-catalog-coordination)

## Basic Setup

### Enable Redis

To enable Redis in LibreChat, set the following environment variable in your `.env` file:

```bash
USE_REDIS=true
```

**Important**: When `USE_REDIS=true`, you must also provide a `REDIS_URI`. The application will throw an error if Redis is enabled without a connection URI.

## Connection Types

### Single Redis Instance

For a standard single Redis server setup:

```bash
# Local Redis instance
REDIS_URI=redis://127.0.0.1:6379

# Remote Redis instance
REDIS_URI=redis://your-redis-host:6379
```

### Redis Cluster

For Redis cluster deployments with multiple nodes:

```bash
# Multiple Redis cluster nodes
REDIS_URI=redis://127.0.0.1:7001,redis://127.0.0.1:7002,redis://127.0.0.1:7003
```

The application automatically detects cluster mode when multiple URIs are provided.

If your redis cluster only has a single URI, you can use the `USE_REDIS_CLUSTER` environment variable to enable cluster mode:

```bash
# Redis cluster with single URI
REDIS_URI=redis://127.0.0.1:7001
USE_REDIS_CLUSTER=true
```

### Single-Endpoint Managed Redis Services

Some managed Redis services, including AWS ElastiCache Serverless and Redis Enterprise Cloud on AWS, expose a single connection endpoint while sharding keys internally. In that setup, keep LibreChat in single-node connection mode, but enable cluster-safe deletes if cache clears fail with `CROSSSLOT Keys in request don't hash to the same slot`.

```bash
USE_REDIS=true
REDIS_URI=rediss://your-managed-redis-endpoint:6379
USE_REDIS_CLUSTER=false
REDIS_CLUSTER_SAFE_DELETE=true
```

`REDIS_CLUSTER_SAFE_DELETE=true` makes LibreChat delete matching cache keys one at a time instead of sending multi-key `DEL` commands. This avoids `CROSSSLOT` errors without changing how LibreChat connects to Redis.

Use `USE_REDIS_CLUSTER=true` only when LibreChat should create a Redis Cluster client. For single-endpoint managed services, `REDIS_CLUSTER_SAFE_DELETE=true` is the safer option.

### Redis with TLS/SSL

For secure Redis connections:

```bash
# Redis with TLS encryption
REDIS_URI=rediss://127.0.0.1:6380

# Path to CA certificate for TLS verification
REDIS_CA=/path/to/ca-cert.pem
```

## Security Configuration

### Authentication

Configure Redis authentication credentials:

```bash
# Method 1: Include credentials in URI
# With both username and password
REDIS_URI=redis://myuser:mypassword@127.0.0.1:6379


# Method 2: Separate environment variables
REDIS_URI=redis://127.0.0.1:6379
REDIS_USERNAME=your_redis_username
REDIS_PASSWORD=your_redis_password
```

**Note**: Separate username/password variables override credentials in the URI if both are provided.

### TLS Configuration

For encrypted connections:

```bash
# Enable TLS with rediss:// protocol
REDIS_URI=rediss://your-redis-host:6380

# Provide CA certificate for verification
REDIS_CA=/path/to/your/ca-certificate.pem
```

### TLS with Elasticache

Elasticache may need to use an alternate dnsLookup for TLS connections. see "Special Note: Aws Elasticache Clusters with TLS" on this webpage: https://www.npmjs.com/package/ioredis

```bash
# Enable redis alternate dnsLookup
REDIS_USE_ALTERNATIVE_DNS_LOOKUP=true
```

## Advanced Options

### Key Prefixing

Redis key prefixing prevents cross-deployment contamination by isolating cache data between different environments, versions, or instances sharing the same Redis server. This is essential for:

- **Multi-tenant deployments**: Separate staging, production, and development environments
- **Blue-green deployments**: Isolate cache between different application versions

```bash
# Option 1: Dynamic prefix from environment variable (recommended for cloud)

# Google Cloud Platform - Cloud Run
REDIS_KEY_PREFIX_VAR=K_REVISION

# AWS - ECS/Fargate
REDIS_KEY_PREFIX_VAR=AWS_EXECUTION_ENV

# Azure Container Instances
REDIS_KEY_PREFIX_VAR=CONTAINER_NAME

# Kubernetes - Pod name or deployment
REDIS_KEY_PREFIX_VAR=HOSTNAME
REDIS_KEY_PREFIX_VAR=POD_NAME

# Kubernetes - Custom deployment identifier
REDIS_KEY_PREFIX_VAR=DEPLOYMENT_ID

# Heroku
REDIS_KEY_PREFIX_VAR=DYNO

# Option 2: Static prefix (for manual control)
REDIS_KEY_PREFIX=librechat-prod-v2
REDIS_KEY_PREFIX=staging-branch-feature-x
REDIS_KEY_PREFIX=dev-john-local
```

**Important**: You cannot set both `REDIS_KEY_PREFIX_VAR` and `REDIS_KEY_PREFIX` simultaneously.

**Examples of contamination without prefixing**:

- Production cache overwritten by staging deployment
- Feature branch tests corrupting main branch cache
- Old deployment versions serving stale cached data

**Key prefixing format**:

- IoRedis client: `{prefix}::{key}`
- Keyv client: Handled by the store layer

### Connection Limits

Configure Redis connection limits:

```bash
# Maximum number of event listeners (default: 40)
REDIS_MAX_LISTENERS=40
```

### Connection Keep-Alive

Configure Redis ping intervals to maintain connections:

```bash
# Redis ping interval in seconds (default: 0 = disabled)
# When set to a positive integer (in seconds), Redis clients will ping the server at this interval
# When unset or 0, no pinging is performed (recommended for most use cases)
# Example: 300 = ping every 5 minutes
REDIS_PING_INTERVAL=300
```

**Important**:

- Setting `REDIS_PING_INTERVAL=0` or omitting it disables pinging entirely
- Only set a positive value (in seconds) if you experience connection timeout issues
- The interval is specified in seconds and applies to both IoRedis and Keyv Redis clients
- Example values: `300` (5 minutes), `600` (10 minutes), `60` (1 minute)

### Replica Failover Recovery

During a Sentinel failover, a demoted Redis replica can keep an existing socket open while rejecting writes with `READONLY`. LibreChat detects this response on the non-cluster Keyv client, tears down the stale connection, and reconnects so the current primary can be resolved without restarting the application.

```bash
# Minimum interval between forced recovery attempts, in milliseconds
# Default: 5000
REDIS_READONLY_RECOVERY_INTERVAL=5000
```

The interval debounces reconnect attempts during a burst of failed writes. Redis Cluster clients are excluded because they use native cluster topology and redirect handling.

### Selective In-Memory Caching

Force specific cache namespaces to use in-memory storage even when Redis is enabled:

```bash
# Comma-separated list of cache keys
FORCED_IN_MEMORY_CACHE_NAMESPACES=ROLES,MESSAGES
```

Valid cache keys (from the `CacheKeys` enum in `librechat-data-provider`):

| Key                       | Description               |
| ------------------------- | ------------------------- |
| `CONFIG_STORE`            | Configuration store       |
| `ROLES`                   | User roles                |
| `PLUGINS`                 | Plugins data              |
| `GEN_TITLE`               | Generated titles          |
| `TOOLS`                   | Tools data                |
| `MODELS_CONFIG`           | Models configuration      |
| `MODEL_QUERIES`           | Model queries             |
| `STARTUP_CONFIG`          | Startup configuration     |
| `ENDPOINT_CONFIG`         | Endpoint configuration    |
| `TOKEN_CONFIG`            | Token configuration       |
| `APP_CONFIG`              | Application configuration |
| `ABORT_KEYS`              | Abort keys                |
| `BANS`                    | Ban data                  |
| `ENCODED_DOMAINS`         | Encoded domains           |
| `AUDIO_RUNS`              | Audio processing runs     |
| `MESSAGES`                | Messages                  |
| `FLOWS`                   | Flows data                |
| `PENDING_REQ`             | Pending requests          |
| `S3_EXPIRY_INTERVAL`      | S3 expiry intervals       |
| `OPENID_EXCHANGED_TOKENS` | OpenID exchanged tokens   |
| `OPENID_SESSION`          | OpenID sessions           |
| `SAML_SESSION`            | SAML sessions             |

<Callout type="warn" title="Invalid keys">
  Using an invalid key (e.g., the deprecated `STATIC_CONFIG`) will cause a startup error. Only use
  keys from the table above.
</Callout>

## Performance Tuning

### Connection Keep-Alive

The application implements configurable connection keep-alive:

- Ping intervals are controlled by `REDIS_PING_INTERVAL` environment variable
- Default behavior: No pinging (recommended for most deployments)
- When enabled, pings both IoRedis and Keyv Redis clients at the specified interval
- Automatically clears ping intervals on disconnect/close events

### Cache Strategy

The application uses a dual-client approach:

- **IoRedis client**: Primary Redis operations with automatic prefixing
- **Keyv Redis client**: Store-layer operations with prefix handling in `cacheFactory.js`

### Memory Optimization

Use `FORCED_IN_MEMORY_CACHE_NAMESPACES` to optimize performance by keeping frequently accessed, small datasets in memory while using Redis for larger caches.

## Configuration Examples

### Development Setup

```bash
USE_REDIS=true
REDIS_URI=redis://127.0.0.1:6379
REDIS_KEY_PREFIX=librechat-dev
```

### Production Setup

```bash
USE_REDIS=true
REDIS_URI=rediss://prod-redis.company.com:6380
REDIS_USERNAME=librechat_user
REDIS_PASSWORD=secure_password_here
REDIS_CA=/etc/ssl/redis-ca.pem
REDIS_KEY_PREFIX_VAR=DEPLOYMENT_ID
REDIS_MAX_LISTENERS=100
REDIS_PING_INTERVAL=300
FORCED_IN_MEMORY_CACHE_NAMESPACES=ROLES,MESSAGES
```

### Cluster Setup

```bash
USE_REDIS=true
REDIS_URI=redis://cluster-node1:7001,redis://cluster-node2:7002,redis://cluster-node3:7003
REDIS_USERNAME=cluster_user
REDIS_PASSWORD=cluster_password
REDIS_KEY_PREFIX=librechat-cluster
```

## Resumable Streams

Redis enables [Resumable Streams](/docs/features/resumable_streams) for horizontally scaled deployments. When enabled, AI responses can seamlessly reconnect and resume across server instances.

**Important:** When `USE_REDIS=true`, resumable streams automatically use Redis for cross-instance coordination. This is the recommended setup for horizontally scaled deployments where users might connect to different server instances.

**Note:** If you're running a single LibreChat instance, Redis for resumable streams is typically overkill—the built-in in-memory mode works fine. Redis becomes essential when you have multiple LibreChat instances behind a load balancer, where a user's reconnection might hit a different server than where their stream started.

### Configuration

```bash
# Redis enabled = resumable streams automatically use Redis
USE_REDIS=true
REDIS_URI=redis://127.0.0.1:6379

# Optional: explicitly control resumable streams behavior
# USE_REDIS_STREAMS=true  # Enabled by default when USE_REDIS=true
```

### Generation Protocol Compatibility

Current LibreChat clients negotiate generation protocol v2 automatically with every built-in generation store. No deployment setting is required. Existing generations with an explicit or stored protocol-v1 marker keep their immutable v1 behavior through steering, pause/resume, retry, and recovery until they finish.

For a rolling upgrade, every running API and worker image must include the v2 compatibility bridge:

- Every API and worker image runs LibreChat `v0.8.8-rc1` or newer.
- Helm chart `2.0.8` or newer selects a compatible default image; a custom image override must still meet the image requirement above.

When upgrading from an older release, perform a cold handoff: stop every old API and worker replica before starting the new image. This prevents pre-v2 and automatic-v2 binaries from sharing generation state in Redis.

<Callout type="warning" title="Rolling deployment safety">
  Do not run a release older than `v0.8.8-rc1` against the same Redis generation state as current replicas. Before rolling back below that compatibility floor, first ensure current replicas and protocol-v2 generations have drained.
</Callout>

In-memory generation streams also negotiate protocol v2 automatically and do not share generation state across processes.

### Stream Delta Coalescing

Redis-backed streams can batch delta publications to reduce Redis scripts, round trips, and CPU at high token rates:

```bash filename=".env"
STREAM_DELTA_COALESCE_MS=25
```

The setting is off when unset or set to `0`, `25` milliseconds is recommended, and values above `1000` are capped. Batching can add up to one configured window of delivery latency.

<Callout type="warning" title="Rolling deployment safety">
  Enable coalescing only after every LibreChat replica supports batch frames. Older subscribers drop coalesced frames.
</Callout>

### Key Benefits (for Horizontal Scaling)

- **Cross-instance continuity**: Users can start a generation on one server and resume on another
- **Rolling deployments**: Active streams survive server restarts
- **Multi-tab sync**: Same conversation syncs across multiple browser tabs in a load-balanced environment
- **Connection resilience**: Automatic reconnection regardless of which server handles the request

### Cluster Configuration

For Redis Cluster deployments, LibreChat automatically uses hash-tagged keys to ensure stream operations stay within the same cluster slot:

```bash
USE_REDIS=true
USE_REDIS_STREAMS=true
USE_REDIS_CLUSTER=true
REDIS_URI=redis://node1:7001,redis://node2:7002,redis://node3:7003
```

See [Resumable Streams](/docs/features/resumable_streams) for more details on this feature.

## MCP Catalog Coordination

When Redis is enabled, LibreChat coordinates application-level MCP tool catalogs across replicas. Startup publication, reconnect synchronization, and `notifications/tools/list_changed` updates use generation and revision markers so a stale replica cannot overwrite a newer catalog.

Redis Cluster deployments use slot-safe catalog keys and update operations; no separate MCP Redis setting is required. See [MCP Server Management](/docs/features/mcp#mcp-server-management) for dynamic catalog and OAuth readiness behavior.


# File Storage & CDN (https://www.librechat.ai/docs/configuration/cdn)

import { S3Icon, AzureIcon, AWSIcon, FirebaseIcon } from '@/components/icons/providers'

<Callout type="info" title="Note">
  LibreChat supports local storage, object storage backends, and CDN-backed delivery. Use an object
  storage backend such as S3 or Azure Blob Storage for durable file storage, then add a CDN like
  CloudFront when you need stable media links, edge caching, signed cookies, or signed download URLs.
</Callout>

## File Storage

Durable object storage backends for user uploads, generated images, and other files.

<Cards num={2}>
  <Cards.Card icon={<S3Icon />} title="Amazon S3" href="/docs/configuration/cdn/s3" arrow>
    Set up an Amazon S3 bucket as a durable, scalable object storage backend.
  </Cards.Card>
  <Cards.Card icon={<AzureIcon />} title="Azure Blob Storage" href="/docs/configuration/cdn/azure" arrow>
    Use Azure Blob Storage containers as your file storage backend.
  </Cards.Card>
</Cards>

## CDN

Edge-cached delivery for stable media links, signed cookies, and signed download URLs.

<Cards num={2}>
  <Cards.Card icon={<AWSIcon />} title="CloudFront with S3" href="/docs/configuration/cdn/cloudfront" arrow>
    Serve S3-backed files through CloudFront for edge caching and signed URLs.
  </Cards.Card>
  <Cards.Card icon={<FirebaseIcon />} title="Firebase CDN" href="/docs/configuration/cdn/firebase" arrow>
    Deliver files through Firebase Storage backed by Google's CDN.
  </Cards.Card>
</Cards>


# Amazon S3 (https://www.librechat.ai/docs/configuration/cdn/s3)

Amazon S3 is a scalable, secure object storage service that can be used as a file storage backend for LibreChat. Follow these steps to configure your S3 bucket.

## 1. Create an AWS Account and Configure an IAM User (or Use IRSA)

### Option A: Using an IAM User with Explicit Credentials

1. **Sign in to AWS:**
   - Open the [AWS Management Console](https://aws.amazon.com/console/) and sign in with your account.

2. **Create or Use an Existing IAM User:**
   - Navigate to the **IAM (Identity and Access Management)** section.
   - Create a new IAM user with **Programmatic Access** or select an existing one.
   - Attach an appropriate policy (for example, `AmazonS3FullAccess` or a custom policy with limited S3 permissions).
   - After creating the user, you will receive an **AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY**. Store these securely.

### Option B: Using IRSA (IAM Roles for Service Accounts) in Kubernetes

If you are deploying LibreChat on Kubernetes (e.g. on EKS), you can use IRSA to assign AWS permissions to your pods without having to provide explicit credentials. To use IRSA:

1. **Create a Trust Policy** for your EKS service account (example below):
   ```json
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Federated": "arn:aws:iam::{AWS_ACCOUNT}:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/{EKS_OIDC}"
         },
         "Action": "sts:AssumeRoleWithWebIdentity",
         "Condition": {
           "StringEquals": {
             "oidc.eks.us-east-1.amazonaws.com/id/{EKS_OIDC}:sub": "system:serviceaccount:librechat:librechat",
             "oidc.eks.us-east-1.amazonaws.com/id/{EKS_OIDC}:aud": "sts.amazonaws.com"
           }
         }
       }
     ]
   }
   ```
2. **Create a Policy** that grants necessary S3 permissions (example below):

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObjectAcl",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::my-example-librechat-bucket/*",
        "arn:aws:s3:::my-example-librechat-bucket"
      ]
    }
  ]
}
```

3. **Annotate your Kubernetes ServiceAccount:**  
   Ensure your LibreChat pods use a service account annotated for IRSA. This way, the AWS SDK in your application (using our updated S3 initialization code) will automatically use the temporary credentials provided by IRSA without needing the environment variables for AWS credentials.

## 2. Create an S3 Bucket

1. **Open the S3 Console:**

- Go to the [Amazon S3 console](https://s3.console.aws.amazon.com/s3/).

2. **Create a New Bucket:**

- Click **"Create bucket"**.
- **Bucket Name:** Enter a unique name (e.g., `mylibrechatbucket`).
- **Region:** Select the AWS region closest to your users (for example, `us-east-1` or `eu-west-1`).
- **Configure Options:** Set other options as needed, then click **"Create bucket"**.

## 3. Update Your Environment Variables

If you are **not** using IRSA, create or update your `.env` file in your project’s root directory with the following configuration:

```bash filename=".env"
AWS_ACCESS_KEY_ID=your_access_key_id
AWS_SECRET_ACCESS_KEY=your_secret_access_key
AWS_REGION=your_selected_region
AWS_BUCKET_NAME=your_bucket_name
AWS_ENDPOINT_URL=https://your_endpoint_url
# AWS_FORCE_PATH_STYLE=false
```

- **AWS_ACCESS_KEY_ID:** Your IAM user's access key.
- **AWS_SECRET_ACCESS_KEY:** Your IAM user's secret key.
- **AWS_REGION:** The AWS region where your S3 bucket is located.
- **AWS_BUCKET_NAME:** The name of the S3 bucket you created.
- **AWS_ENDPOINT_URL:** (Optional) The custom AWS endpoint URL. Required for S3-compatible services such as MinIO, Cloudflare R2, Hetzner Object Storage, Backblaze B2, and IDrive e2. Include the URL scheme, such as `https://a7g8.da.idrivee2-32.com`; values without `http://` or `https://` can cause an `Invalid URL` error when files are streamed.
- **AWS_FORCE_PATH_STYLE:** (Optional) Set to `true` for providers that require path-style URLs (`endpoint/bucket/key`) rather than virtual-hosted-style (`bucket.endpoint/key`). Required for Hetzner Object Storage, MinIO, and similar providers whose SSL certificates don't cover bucket subdomains. Not needed for AWS S3 or Cloudflare R2. Default: `false`.
- **S3_URL_EXPIRY_SECONDS:** (Optional) Lifetime of each presigned URL, in seconds. See the note on presigned URLs below for the provider-side caps that apply.
- **S3_REFRESH_EXPIRY_MS:** (Optional) Regenerate a presigned URL once it reaches this age, in milliseconds, instead of using the default expiry-buffer logic. Unset by default. A value that is not a positive integer is ignored with a warning in the logs.

If you are using **IRSA** on Kubernetes, you do **not** need to set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your environment. The AWS SDK will automatically obtain temporary credentials via the service account assigned to your pod. Ensure that `AWS_REGION` and `AWS_BUCKET_NAME` are still provided.

## 4. Configure LibreChat to Use Amazon S3

Update your LibreChat configuration file (`librechat.yaml`) to specify that the application should use Amazon S3 for file handling:

```yaml filename="librechat.yaml"
version: 1.3.11
cache: true
fileStrategy: 's3'
```

<Callout type="warning" title="S3 presigned URLs expire for visual assets">
  S3 does not serve files through a CDN. LibreChat accesses S3 files via **presigned URLs**, which are temporary signed tokens with a configurable expiry (`S3_URL_EXPIRY_SECONDS`). AWS caps presigned URL lifetime at 7 days for IAM user credentials, and just a few hours when using temporary credentials (STS/IAM roles such as IRSA). Once a URL expires, the image or avatar it references will appear broken in the UI until the page is refreshed and a new URL is generated.

The refresh logic in LibreChat is not applied consistently for every visual surface. For example, list-style endpoints can return stored URLs while detail endpoints refresh them. This can cause visible broken avatar images in the model selector and chat UI. See the [related discussion](https://github.com/danny-avila/LibreChat/discussions/10280#discussioncomment-14803903) for full context.

**S3 is well-suited for document storage** (PDFs, text files, code) where short-lived presigned download URLs are appropriate. For images and avatars that need to render persistently across the UI, use [CloudFront with S3](/docs/configuration/cdn/cloudfront), [Firebase](/docs/configuration/cdn/firebase), or configure `fileStrategies` to route only those types to a CDN-backed strategy:

```yaml filename="librechat.yaml"
fileStrategies:
  avatar: 'cloudfront'
  image: 'cloudfront'
  document: 's3'
```

</Callout>

## Summary

1. **Create an AWS Account & IAM User (or configure IRSA):**

- For traditional deployments, create an IAM user with programmatic access and obtain your access keys.
- For Kubernetes deployments (e.g., on EKS), set up IRSA so that your pods automatically obtain temporary credentials.

2. **Create an S3 Bucket:**

- Use the Amazon S3 console to create a bucket, choosing a unique name and region.

3. **Update Environment Variables:**

- For non-IRSA: set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, and `AWS_BUCKET_NAME` in your `.env` file.
- For IRSA: set only `AWS_REGION` and `AWS_BUCKET_NAME`; ensure your pod’s service account is correctly annotated.

4. **Configure LibreChat:**

- Set `fileStrategy` to `"s3"` in your `librechat.yaml` configuration file, or use `fileStrategies` to keep documents in S3 while sending images and avatars through CloudFront.

With these steps, your LibreChat application will use Amazon S3 to handle file uploads, downloads, and deletions. Additionally, with IRSA support, your application can run securely on Kubernetes without embedding long-term AWS credentials.

<Callout type="info" title="Note">
  Always ensure your AWS credentials remain secure. Do not commit them to a public repository.
  Adjust IAM policies to follow the principle of least privilege as needed.
</Callout>


# CloudFront with S3 (https://www.librechat.ai/docs/configuration/cdn/cloudfront)

CloudFront lets LibreChat keep files in S3 while serving images, avatars, and downloads through stable CDN URLs. This is the recommended AWS setup when you want S3 durability without exposing users to expiring S3 presigned image URLs.

## When to Use CloudFront

Use CloudFront when you want:

- Stable avatar and image URLs that keep rendering across the UI
- Global edge caching in front of an S3 bucket
- Signed cookies for private inline images and avatars
- Backend-authorized signed URLs for downloads
- Optional cache invalidation when files are deleted

<Callout type="info" title="S3 is still required">
  The `cloudfront` file strategy stores objects in S3 and returns CloudFront URLs. Configure the S3
  environment variables first, then add the `cloudfront` block in `librechat.yaml`.
</Callout>

## Requirements

- A private S3 bucket
- A CloudFront distribution with the S3 bucket as an origin
- An Origin Access Control (OAC) or equivalent origin access policy so CloudFront can read from S3
- `AWS_REGION` and `AWS_BUCKET_NAME`
- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, unless your deployment uses an AWS identity provider such as IRSA
- `CLOUDFRONT_KEY_PAIR_ID` and `CLOUDFRONT_PRIVATE_KEY` when using signed cookies or signed download URLs

## Environment Variables

```bash filename=".env"
AWS_ACCESS_KEY_ID=your_access_key_id
AWS_SECRET_ACCESS_KEY=your_secret_access_key
AWS_REGION=us-east-1
AWS_BUCKET_NAME=your_bucket_name

# Required for signed cookies and signed CloudFront download URLs
CLOUDFRONT_KEY_PAIR_ID=K1234567890ABC
CLOUDFRONT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
```

`CLOUDFRONT_PRIVATE_KEY` must contain the full PEM private key. In `.env`, quote it and preserve newlines, or inject it from your platform secret manager.

## Basic Configuration

Use `fileStrategies` when you want CloudFront for images and avatars while keeping documents on S3 signed URLs:

```yaml filename="librechat.yaml"
version: 1.3.11

fileStrategies:
  avatar: 'cloudfront'
  image: 'cloudfront'
  document: 's3'

cloudfront:
  domain: 'https://cdn.example.com'
  imageSigning: 'none'
  urlExpiry: 3600
```

Use `fileStrategy` if every file type should use CloudFront:

```yaml filename="librechat.yaml"
fileStrategy: 'cloudfront'

cloudfront:
  domain: 'https://cdn.example.com'
```

## Signed Cookies

Signed cookies are the secure mode for private inline images and avatars. They let LibreChat keep stable CloudFront URLs in messages and records while authorizing access with short-lived cookies.

```yaml filename="librechat.yaml"
fileStrategies:
  avatar: 'cloudfront'
  image: 'cloudfront'
  document: 's3'

cloudfront:
  domain: 'https://cdn.example.com'
  imageSigning: 'cookies'
  cookieDomain: '.example.com'
  cookieExpiry: 1800
  urlExpiry: 3600
  requireSignedAccess: true
```

### Domain Requirements

For signed cookies, the LibreChat API and CloudFront hostname must share a parent domain:

- API: `https://api.example.com`
- CloudFront CNAME: `https://cdn.example.com`
- `cookieDomain: ".example.com"`

`cookieDomain` must start with a dot. The browser will not send CloudFront cookies to an unrelated domain.

### What Cookies Protect

LibreChat scopes signed cookies to inline media paths:

- `/i/...` private uploaded or generated images, scoped to the user
- `/a/...` avatar assets, scoped to the tenant when `tenantId` is present

Documents, general uploads, and code outputs stay outside those inline media paths. Downloads are authorized by the backend and returned as signed CloudFront URLs.

### Cookie Refresh

When signed-cookie mode is active, LibreChat advertises a cookie refresh endpoint in startup config:

```text
POST /api/auth/cloudfront/refresh
```

Authenticated sessions refresh cookies during auth flows, token refresh, and CloudFront image retry paths. The refresh response includes the cookie lifetime and the recommended refresh timing.

## Signed Downloads

LibreChat uses signed CloudFront URLs for authorized downloads. The `urlExpiry` setting controls their lifetime in seconds:

```yaml filename="librechat.yaml"
cloudfront:
  domain: 'https://cdn.example.com'
  imageSigning: 'cookies'
  cookieDomain: '.example.com'
  urlExpiry: 3600
```

For direct-download filename and content-type overrides, configure the CloudFront cache/origin request policy to forward these query strings to S3:

- `response-content-disposition`
- `response-content-type`

For download paths, attach a response headers policy with:

- `X-Content-Type-Options: nosniff`
- A restrictive Content Security Policy, such as `default-src 'none'`

## Cache Invalidation

By default, LibreChat deletes the S3 object and does not create a CloudFront invalidation. Enable invalidation when deleted files must disappear from edge cache immediately:

```yaml filename="librechat.yaml"
cloudfront:
  domain: 'https://cdn.example.com'
  distributionId: 'E1234ABCD'
  invalidateOnDelete: true
```

`distributionId` is required when `invalidateOnDelete` is `true`. The AWS identity used by LibreChat also needs `cloudfront:CreateInvalidation`.

## Multi-Region Object Paths

`includeRegionInPath` adds the storage region to newly generated object keys:

```yaml filename="librechat.yaml"
cloudfront:
  domain: 'https://cdn.example.com'
  storageRegion: 'us-east-2'
  includeRegionInPath: true
```

When enabled, new keys include region-aware path segments, for example:

```text
/i/r/us-east-2/t/tenantId/images/userId/file.png
/a/r/us-east-2/t/tenantId/avatars/userId/avatar.png
/r/us-east-2/t/tenantId/images/userId/file.pdf
```

This only affects newly generated keys. Existing files are not moved. LibreChat does not configure CloudFront origins, Route 53, or regional routing for you.

## CloudFront Block Reference

<OptionTable
  options={[
    [
      'domain',
      'string',
      'CloudFront distribution domain or CNAME. Required.',
      'domain: "https://cdn.example.com"',
    ],
    [
      'distributionId',
      'string',
      'Distribution ID used for cache invalidations.',
      'distributionId: "E1234ABCD"',
    ],
    [
      'invalidateOnDelete',
      'boolean',
      'Create a CloudFront invalidation when a file is deleted. Default: false.',
      'invalidateOnDelete: false',
    ],
    [
      'imageSigning',
      'string',
      'Inline media access mode. Use `"none"` for public CloudFront access or `"cookies"` for signed cookies. `"url"` is reserved and not implemented for images.',
      'imageSigning: "cookies"',
    ],
    [
      'cookieDomain',
      'string',
      'Shared parent domain for signed cookies. Required when `imageSigning` is `"cookies"`.',
      'cookieDomain: ".example.com"',
    ],
    [
      'cookieExpiry',
      'number',
      'Signed cookie lifetime in seconds. Default: 1800. Maximum: 604800.',
      'cookieExpiry: 1800',
    ],
    [
      'urlExpiry',
      'number',
      'Signed download URL lifetime in seconds. Default: 3600.',
      'urlExpiry: 3600',
    ],
    [
      'storageRegion',
      'string',
      'Optional region label for region-aware object paths.',
      'storageRegion: "us-east-2"',
    ],
    [
      'includeRegionInPath',
      'boolean',
      'Include `storageRegion` in newly generated object keys. Default: false.',
      'includeRegionInPath: false',
    ],
    [
      'requireSignedAccess',
      'boolean',
      'Fail startup if signed-cookie CloudFront access cannot initialize. Default: false.',
      'requireSignedAccess: true',
    ],
  ]}
/>

## Suggested AWS Permissions

Use least-privilege IAM permissions for the S3 bucket. Add CloudFront invalidation permission only if `invalidateOnDelete` is enabled.

```json filename="iam-policy.json"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::my-librechat-bucket", "arn:aws:s3:::my-librechat-bucket/*"]
    },
    {
      "Effect": "Allow",
      "Action": "cloudfront:CreateInvalidation",
      "Resource": "arn:aws:cloudfront::123456789012:distribution/E1234ABCD"
    }
  ]
}
```

## Troubleshooting

- Startup logs `CloudFront domain is required`: add `cloudfront.domain`.
- Startup logs `S3 must be initialized`: configure S3 environment variables first.
- Signed cookies are not set: confirm `imageSigning: "cookies"`, `cookieDomain`, `CLOUDFRONT_KEY_PAIR_ID`, and `CLOUDFRONT_PRIVATE_KEY`.
- Browser still cannot load images: confirm API and CDN hostnames share the configured parent domain and that cookies are allowed with `Secure` and `SameSite=None`.
- Downloads ignore filename/content type: update the CloudFront cache/origin request policy to forward the response override query strings.


# Azure Blob Storage (https://www.librechat.ai/docs/configuration/cdn/azure)

{/* Adding a table of contents for better navigation */}
<div className="mt-8 mb-16">
  <h2 className="text-lg font-semibold mb-4">On this page</h2>
  <ul className="space-y-2">
    <li><a href="#production-setup">Production Setup</a></li>
    <li><a href="#local-development-with-azurite">Local Development with Azurite</a></li>
  </ul>
</div>

## Production Setup

Azure Blob Storage offers scalable, secure object storage for files in LibreChat. Follow these steps to configure your Azure Blob Storage.

### What uses this

Setting a file strategy changes where LibreChat puts every file it stores, rather than enabling a particular feature. That covers user and agent avatars, files uploaded in chat, images returned by the image generation tools, and files produced by the Code Interpreter.

The default strategy writes all of that to the API container's filesystem. The standard Docker Compose deployment bind-mounts `./images` and `./uploads` to the host, so on a single-instance compose setup those files already survive recreating the container. What local storage cannot do is share files across instances, and it does lose them on any deployment that leaves those paths on the container's writable layer, such as Kubernetes without a persistent volume. Configure Azure Blob Storage (or another provider under [CDN](/docs/configuration/cdn)) when you run more than one instance, or when nothing persistent is mounted behind those paths.

## 1. Create an Azure Storage Account

1. **Sign in to Azure:**
   - Open the [Azure Portal](https://portal.azure.com/) and sign in with your Microsoft account.

2. **Create a Storage Account:**
   - Click on **"Create a resource"** and search for **"Storage account"**.
   - Click **"Create"** and fill in the required details:
     - **Subscription & Resource Group:** Choose your subscription and either select an existing resource group or create a new one.
     - **Storage Account Name:** Enter a unique name (e.g., `mylibrechatstorage`).
     - **Region:** Select the region closest to your users.
     - **Performance & Redundancy:** Choose the performance tier and redundancy level that best suit your needs.
   - Click **"Review + Create"** and then **"Create"**. Wait until the deployment completes.

## 2. Set Up Authentication

You have two options for authenticating with your Azure Storage Account:

### Option A: Using a Connection String

1. **Navigate to Access Keys:**
   - In your newly created storage account, go to **"Access keys"** in the sidebar.

2. **Copy Connection String:**
   - Copy one of the connection strings provided. This string includes the credentials required to connect to your Blob Storage account.

### Option B: Using Managed Identity

If your LibreChat application is running on an Azure service that supports Managed Identity (such as an Azure VM, App Service, or AKS), you can use that instead of a connection string.

1. **Assign Managed Identity:**
   - Ensure your Azure resource (VM, App Service, or AKS) has a system-assigned or user-assigned Managed Identity enabled.

2. **Grant Storage Permissions:**
   - In your storage account, assign the **Storage Blob Data Contributor** (or a similarly scoped role) to your Managed Identity. This allows your application to access Blob Storage without a connection string.

## 3. Update Your Environment Variables

Create or update your `.env` file in your project’s root with the following configuration:

```bash filename=".env"
# Option A: Using a Connection String
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=yourAccountName;AccountKey=yourAccountKey;EndpointSuffix=core.windows.net

# Option B: Using Managed Identity (do not set the connection string if using Managed Identity)
AZURE_STORAGE_ACCOUNT_NAME=yourAccountName

AZURE_STORAGE_PUBLIC_ACCESS=false
AZURE_CONTAINER_NAME=files
```

- **AZURE_STORAGE_CONNECTION_STRING:** Set this if you are using Option A.
- **AZURE_STORAGE_ACCOUNT_NAME:** Set this if you are using Option B (Managed Identity). Do not set both.
- **AZURE_STORAGE_PUBLIC_ACCESS:** Set to `false` if you do not want your blobs to be publicly accessible by default. Set to `true` if you need public access (for example, for publicly viewable images).
- **AZURE_CONTAINER_NAME:** This is the container name your application will use (e.g., `files`). The application will automatically create this container if it doesn’t exist.

## 4. Configure LibreChat to Use Azure Blob Storage

Update your LibreChat configuration file (`librechat.yaml`) to specify that the application should use Azure Blob Storage for file handling:

```yaml filename="librechat.yaml"
version: 1.3.5
cache: true
fileStrategy: "azure_blob"
```

<Callout type="warning" title="Azure Blob Storage is object storage, not a CDN">
  Azure Blob Storage stores and serves files directly from origin — it is not a CDN. Images and avatars are best served through a CDN for optimal performance and global delivery. Currently, [Firebase](/docs/configuration/cdn/firebase) is the only CDN-backed storage option.

  You can use `fileStrategies` to route only avatars and images to Firebase while keeping documents on Azure Blob Storage:

  ```yaml filename="librechat.yaml"
  fileStrategies:
    avatar: "firebase"
    image: "firebase"
    document: "azure_blob"
  ```
</Callout>

---

## Summary

1. **Create a Storage Account:**  
Sign in to the Azure Portal, create a storage account, and wait for deployment to finish.

2. **Set Up Authentication:**
- **Option A:** Retrieve the connection string from **"Access keys"** in your storage account.
- **Option B:** Use Managed Identity by enabling it on your Azure resource and granting it appropriate storage permissions.

3. **Update Environment Variables:**  
In your `.env` file, set either:
- `AZURE_STORAGE_CONNECTION_STRING` (for Option A), or
- `AZURE_STORAGE_ACCOUNT_NAME` (for Option B), along with:
- `AZURE_STORAGE_PUBLIC_ACCESS` and
- `AZURE_CONTAINER_NAME`.

4. **Configure LibreChat:**  
Set `fileStrategy` to `"azure_blob"` in your `librechat.yaml` configuration file.

With these steps, your LibreChat application will automatically create the container (if it doesn't exist) and manage file uploads, downloads, and deletions using Azure Blob Storage. Managed Identity provides a secure alternative by eliminating the need for long-term credentials.

## Local Development with Azurite

For local development and testing, you can use [Azurite](https://github.com/Azure/Azurite), an Azure Storage emulator that provides a local environment for testing your Azure Blob Storage integration without needing an actual Azure account.

### 1. Set Up Azurite

You can run Azurite in several ways:

#### Option A: Using VS Code Extension (Recommended for Development)

1. Install the [Azurite extension](https://marketplace.visualstudio.com/items?itemName=Azurite.azurite) for VS Code
2. Open the command palette (Ctrl+Shift+P or Cmd+Shift+P)
3. Search for and select "Azurite: Start"

This will start Azurite in the background with default settings.

#### Option B: Using Docker

```bash
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite
```

#### Option C: Using npm

```bash
npm install -g azurite
azurite --silent --location /path/to/azurite/workspace --debug /path/to/debug/log
```

### 2. Configure Environment Variables for Local Development

Add the following environment variables to your `.env` file:

```bash filename=".env"
# Azurite connection string for local development
AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
AZURE_STORAGE_PUBLIC_ACCESS=true
AZURE_CONTAINER_NAME=files
```

Notes:
- The `AccountKey` value is the default development key used by Azurite
- The connection uses `http` protocol instead of `https` for local development
- The `BlobEndpoint` points to the local Azurite instance running on port 10000

### 3. Verify the Connection

To verify that your application can connect to the local Azurite instance:

1. Start your LibreChat application
2. Attempt to upload a file through the interface
3. Check the Azurite logs to confirm the connection and operations

If you're using the VS Code extension, you can view the Azurite logs in the Output panel by selecting "Azurite Blob" from the dropdown.

<Callout type="info" title="Note">
  The default Azurite account key is a fixed value used for development purposes only. Never use this key in production environments. Always ensure that your connection string remains secure and never commit it to a public repository.
</Callout>


# Firebase CDN (https://www.librechat.ai/docs/configuration/cdn/firebase)

Firebase Storage integrates with Firebase Hosting's global CDN, letting you serve files stored in Firebase Storage through edge locations around the world. It is one of LibreChat's CDN-backed file storage options, alongside [CloudFront for S3](/docs/configuration/cdn/cloudfront).

<Callout type="info" title="What you'll need">
A Google account and roughly 10 minutes. You'll create a Firebase project, enable Cloud Storage, register a web app to obtain credentials, then point LibreChat at it.
</Callout>

## Create a Firebase Project

<Steps>
<Step>

**Open Firebase and sign in.** Go to the [Firebase website](https://firebase.google.com/), click **Get started**, and sign in with your Google account.

</Step>
<Step>

**Name your project.** You can reuse the same project as Google OAuth if you have one.

<Frame>
![Naming the Firebase project](https://github.com/danny-avila/LibreChat/assets/81851188/dccce3e0-b639-41ef-8142-19d24911c65c)
</Frame>

</Step>
<Step>

**Configure Google Analytics (optional).** You can disable Google Analytics for this project.

<Frame>
![Google Analytics toggle](https://github.com/danny-avila/LibreChat/assets/81851188/5d4d58c5-451c-498b-97c0-f123fda79514)
</Frame>

</Step>
<Step>

**Create the project.** Wait 20-30 seconds for provisioning to finish, then click **Continue**.

<Frame>
![Project ready, click Continue](https://github.com/danny-avila/LibreChat/assets/81851188/6929802e-a30b-4b1e-b124-1d4b281d0403)
</Frame>

</Step>
</Steps>

## Enable Cloud Storage

<Steps>
<Step>

**Open All Products.** From the project dashboard, click **All Products**.

<Frame>
![All Products menu](https://github.com/danny-avila/LibreChat/assets/81851188/92866c82-2b03-4ebe-807e-73a0ccce695e)
</Frame>

</Step>
<Step>

**Select Storage**, then click **Get Started**.

<Frame>
![Select Storage](https://github.com/danny-avila/LibreChat/assets/81851188/b22dcda1-256b-494b-a835-a05aeea02e89)
</Frame>

<Frame>
![Storage Get Started](https://github.com/danny-avila/LibreChat/assets/81851188/c3f0550f-8184-4c79-bb84-fa79655b7978)
</Frame>

</Step>
<Step>

**Confirm the security rules.** Click **Next** to continue.

<Frame>
![Security rules step](https://github.com/danny-avila/LibreChat/assets/81851188/2a65632d-fe22-4c71-b8f1-aac53ee74fb6)
</Frame>

</Step>
<Step>

**Choose a Cloud Storage location**, then finish setup and return to the **Project Overview**.

<Frame>
![Select Cloud Storage location](https://github.com/danny-avila/LibreChat/assets/81851188/c094d4bc-8e5b-43c7-96d9-a05bcf4e2af6)
</Frame>

</Step>
</Steps>

## Register a Web App

<Steps>
<Step>

**Add a web app.** On the Project Overview, click **+ Add app** under your project name, then choose **Web**.

<Frame>
![Add a web app](https://github.com/danny-avila/LibreChat/assets/81851188/22dab877-93cb-4828-9436-10e14374e57e)
</Frame>

</Step>
<Step>

**Register the app** and give it a nickname.

<Frame>
![Register the app](https://github.com/danny-avila/LibreChat/assets/81851188/0a1b0a75-7285-4f03-95cf-bf971bd7d874)
</Frame>

</Step>
<Step>

**Copy your `firebaseConfig` values.** Save the displayed configuration somewhere safe.

<Frame>
![Firebase config values](https://github.com/danny-avila/LibreChat/assets/81851188/056754ad-9d36-4662-888e-f189ddb38fd3)
</Frame>

</Step>
<Step>

**Add the values to your `.env` file.** Map each `firebaseConfig` value to the matching variable:

```bash filename=".env"
FIREBASE_API_KEY=api_key                          # apiKey
FIREBASE_AUTH_DOMAIN=auth_domain                  # authDomain
FIREBASE_PROJECT_ID=project_id                    # projectId
FIREBASE_STORAGE_BUCKET=storage_bucket            # storageBucket
FIREBASE_MESSAGING_SENDER_ID=messaging_sender_id  # messagingSenderId
FIREBASE_APP_ID=1:your_app_id                     # appId
```

</Step>
</Steps>

## Update Storage Rules

<Steps>
<Step>

**Open Storage rules.** Return to the **Project Overview**, select **Storage**, then open the **Rules** tab.

<Frame>
![Open Storage](https://github.com/danny-avila/LibreChat/assets/32828263/16a0f850-cdd4-4875-8342-ab67bfb59804)
</Frame>

</Step>
<Step>

**Allow read and write access.** Change `allow read, write: if false;` to `if true;` so it matches the rules below:

```js filename="storage.rules"
rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{userId}/{fileName} {
      allow read, write: if true;
    }
  }
}
```

<Frame>
![Updated storage rules](https://github.com/danny-avila/LibreChat/assets/32828263/c190011f-c1a6-47c7-986e-8d309b5f8704)
</Frame>

</Step>
<Step>

**Publish your changes.**

<Frame>
![Publish rules](https://github.com/danny-avila/LibreChat/assets/32828263/5e6a17c3-5aba-419a-a18f-be910b1f25d5)
</Frame>

</Step>
</Steps>

## Configure LibreChat

Set `fileStrategy` to `firebase` in your `librechat.yaml` config file so LibreChat uses Firebase for file storage:

```yaml filename="librechat.yaml"
version: 1.3.5
cache: true
fileStrategy: 'firebase'
```

For more about this file, see the [librechat.yaml guide](/docs/configuration/librechat_yaml).

## Enable CORS for PNG Exports

<Callout type="warning" title="Only needed for PNG exports">
Exporting conversations as PNG fetches images directly from Firebase Storage in the browser. Without a CORS policy that allows your domain, those requests are blocked. Skip this section if you don't export conversations as PNG.
</Callout>

<Steps>
<Step>

**Create the CORS configuration file.** In a text editor, create `cors.json` and allow access from your domain:

```json filename="cors.json"
[
  {
    "origin": ["https://ai.example.com"],
    "method": ["GET", "POST", "DELETE", "PUT"],
    "maxAgeSeconds": 3600
  }
]
```

</Step>
<Step>

**Apply the configuration.** From the directory containing `cors.json`, run the command below, replacing `<your-cloud-storage-bucket>` with your bucket name:

```shell
gsutil cors set cors.json gs://<your-cloud-storage-bucket>
```

</Step>
<Step>

**Verify the settings.** Retrieve the active policy and confirm it matches `cors.json`:

```shell
gsutil cors get gs://<your-cloud-storage-bucket>
```

</Step>
<Step>

**Test it.** Export a conversation as PNG from your allowed origin. If everything is configured correctly, the export succeeds without CORS errors.

</Step>
</Steps>

<Callout type="info" title="Security tip">
Only allow CORS for trusted origins, and limit the methods and headers to what your deployment actually needs.
</Callout>


# SharePoint Integration (https://www.librechat.ai/docs/configuration/sharepoint)

LibreChat provides enterprise-grade integration with SharePoint Online and OneDrive for Business, enabling users to seamlessly browse, select, and attach files from their Microsoft 365 environment directly within conversations.

## Overview

The SharePoint integration allows users to:
- Browse SharePoint document libraries and OneDrive files
- Select multiple files at once (up to 10 by default)
- View real-time download progress
- Attach files from SharePoint to conversations
- Maintain enterprise security with proper access controls

<Callout type="info" title="Enterprise Feature">
This feature requires Microsoft 365/SharePoint Online and is designed for enterprise deployments using Azure Entra ID (formerly Azure AD) authentication.
</Callout>

## Prerequisites

Before configuring SharePoint integration, ensure you have:

1. **Azure Entra ID Authentication** configured and working
2. **Token Reuse** enabled (`OPENID_REUSE_TOKENS=true`)
3. **An exposed API scope** for LibreChat, such as `api://<client-id>/access_as_user`
4. **Admin access** to your Azure tenant for app permissions
5. **HTTPS** enabled (required for production environments)

<Callout type="error" title="Critical Requirement">
SharePoint integration will not function without `OPENID_REUSE_TOKENS=true` as it relies on the on-behalf-of token flow to access Microsoft Graph APIs.
</Callout>

## Azure App Registration Setup

### Step 1: Configure API Permissions

1. Navigate to your app registration in the [Azure Portal](https://portal.azure.com)
2. Go to **API permissions** in the left menu
3. Click **Add a permission**

### Step 2: Expose and Grant a LibreChat API Scope

The on-behalf-of flow needs the initial OpenID access token to target your LibreChat app API, not Microsoft Graph. Expose an API scope so Azure can issue a token with LibreChat as the audience.

1. Go to **Expose an API** in the left menu
2. Set the **Application ID URI** to `api://<client-id>` if it is not already configured
3. Click **Add a scope**
4. Name the scope `access_as_user`
5. Save the scope, then copy the full scope value:

```text
api://<client-id>/access_as_user
```

Then grant that scope to the app registration:

1. Go back to **API permissions**
2. Click **Add a permission**
3. Select **APIs my organization uses**
4. Search for and select your LibreChat app registration
5. Choose **Delegated permissions**
6. Select `access_as_user`
7. Click **Add permissions**

Use the full `api://<client-id>/access_as_user` scope value in `OPENID_SCOPE` later in this guide.

### Step 3: Add SharePoint Permissions

For the file picker interface:

1. Select **SharePoint** from the API list
2. Choose **Delegated permissions**
3. Search for and select:
   - `AllSites.Read` - Read items in all site collections
4. Click **Add permissions**

### Step 4: Add Microsoft Graph Permissions

For file downloads:

1. Click **Add a permission** again
2. Select **Microsoft Graph**
3. Choose **Delegated permissions**
4. Search for and select:
   - `Files.Read.All` - Read all files that user can access
5. Click **Add permissions**

### Step 5: Grant Admin Consent

1. After adding the permissions, you'll see them listed
2. Click **Grant admin consent for [Your Organization]**
3. Confirm the consent in the popup

Your permissions should look like this:

| API / Permissions name | Type | Description | Status |
|------------------------|------|-------------|---------|
| Microsoft Graph - Files.Read.All | Delegated | Read all files that user can access | ✅ Granted |
| SharePoint - AllSites.Read | Delegated | Read items in all site collections | ✅ Granted |
| LibreChat - access_as_user | Delegated | Allow LibreChat to receive an OBO-compatible token | ✅ Granted |

## Environment Configuration

Add the following environment variables to your `.env` file:

```bash filename=".env"
# OpenID token reuse and OBO-compatible audience
OPENID_REUSE_TOKENS=true
OPENID_SCOPE=openid profile email offline_access api://<client-id>/access_as_user
OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true

# Enable SharePoint file picker
ENABLE_SHAREPOINT_FILEPICKER=true

# Your SharePoint tenant base URL
# Format: https://[your-tenant-name].sharepoint.com
SHAREPOINT_BASE_URL=https://contoso.sharepoint.com

# SharePoint scope for the file picker
# Replace 'contoso' with your actual tenant name
SHAREPOINT_PICKER_SHAREPOINT_SCOPE=https://contoso.sharepoint.com/AllSites.Read

# Microsoft Graph scope for file downloads
SHAREPOINT_PICKER_GRAPH_SCOPE=Files.Read.All
```

<Callout type="warning" title="Tenant Name">
Ensure you replace `contoso` in the examples above with your actual SharePoint tenant name. This must match your SharePoint URL exactly.
</Callout>

<Callout type="warning" title="OpenID Scope Audience">
Replace `<client-id>` with your Azure app registration's Application (client) ID. The `api://<client-id>/access_as_user` scope gives Azure an app-specific audience for the OBO assertion. If `OPENID_SCOPE` only includes standard OpenID scopes, Azure may issue a Graph-audience access token that cannot be exchanged again for SharePoint or Graph access.
</Callout>

<Callout type="info" title="Userinfo Token Exchange">
`OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true` lets LibreChat exchange the app-audience access token for a userinfo-compatible token before calling the OpenID userinfo endpoint. This is required for Azure Entra ID setups where the `OPENID_SCOPE` includes the LibreChat API scope above.
</Callout>

## How It Works

### Authentication Flow

1. User authenticates via Azure Entra ID
2. When accessing SharePoint picker, LibreChat exchanges the user's token for SharePoint access
3. Tokens are cached for optimal performance (typically 50 minutes)
4. Separate scopes ensure principle of least privilege

### File Selection Process

1. User clicks "From SharePoint" in the attachment menu
2. SharePoint Online file picker opens in an embedded iframe
3. User browses and selects files using the familiar SharePoint interface; current selections remain checked while opening other folders or switching picker views
4. Selected files are queued for download

### Download Process

1. Files are downloaded in batches (up to 3 concurrent downloads)
2. Progress indicator shows current file and percentage complete
3. Downloaded files are attached to the conversation
4. Failed downloads are retried automatically

## User Experience

### Accessing SharePoint Files

When properly configured, users will see a new option in the file attachment menu:

1. Click the attachment icon in the message input
2. Select "From SharePoint" from the menu
3. The SharePoint file picker will open
4. Browse and select files as needed
5. Click "Select" to begin downloading

### Features Available

- **Multiple file selection**: Select up to 10 files at once
- **Persistent selection**: Build one selection across multiple folders and picker views
- **Familiar interface**: Uses native SharePoint file picker
- **Progress tracking**: See real-time download progress
- **Error handling**: Clear messages for any issues
- **Localization**: Supports multiple languages

## Security Considerations

### Access Control

- Only files the user has permission to access in SharePoint are available
- Respects all SharePoint permissions and policies
- No elevated access or bypassing of security controls

### Token Security

- Uses secure on-behalf-of flow for token exchange
- Tokens are short-lived and automatically refreshed
- No long-term storage of SharePoint credentials

### Scope Isolation

- SharePoint scope limited to read operations only
- Graph API scope restricted to file read access
- Cannot modify or delete files through LibreChat

## Troubleshooting

### Common Issues

#### "From SharePoint" option not appearing

**Cause**: Feature not properly enabled or authentication issues

**Solutions**:
1. Verify `ENABLE_SHAREPOINT_FILEPICKER=true` in `.env`
2. Confirm `OPENID_REUSE_TOKENS=true` is set
3. Check that user is authenticated via Azure Entra ID
4. Restart LibreChat after configuration changes

#### File picker fails to open

**Cause**: Missing or incorrect permissions

**Solutions**:
1. Verify SharePoint permissions are granted in Azure
2. Ensure admin consent was provided
3. Check that `SHAREPOINT_BASE_URL` matches your tenant exactly
4. Confirm `SHAREPOINT_PICKER_SHAREPOINT_SCOPE` uses the full tenant URL, such as `https://contoso.sharepoint.com/AllSites.Read`
5. Confirm HTTPS is enabled in production

#### File picker opens to a blank white page

**Cause**: Azure may be rejecting the on-behalf-of exchange because the OpenID access token has the wrong audience, or because the userinfo token exchange is not enabled.

**Solutions**:
1. Confirm your Azure app registration has an exposed API scope, such as `api://<client-id>/access_as_user`
2. Add that full scope to `OPENID_SCOPE`
3. Set `OPENID_ON_BEHALF_FLOW_FOR_USERINFO_REQUIRED=true`
4. Restart LibreChat and sign in again so Azure issues fresh OpenID tokens

#### Downloads fail or timeout

**Cause**: Graph API permissions or network issues

**Solutions**:
1. Verify `Files.Read.All` permission is granted
2. Check network connectivity to SharePoint
3. Ensure tokens haven't expired (re-authenticate if needed)
4. Check browser console for specific error messages

### Debug Mode

For troubleshooting, enable debug logging:

```bash filename=".env"
DEBUG_LOGGING=true
DEBUG_CONSOLE=true
```

This will provide detailed logs about:
- Token exchange processes
- API calls to SharePoint and Graph
- Download progress and errors
- Authentication flows

## Performance Optimization

### Token Caching

- Tokens are cached to reduce authentication overhead
- Cache duration matches token lifetime (typically 50 minutes)
- Automatic refresh before expiration

### Concurrent Downloads

- Up to 3 files download simultaneously
- Prevents overwhelming the browser or server
- Optimizes for both speed and stability

### File Size Considerations

- Large files may take time to download
- Progress indicator helps manage user expectations
- Consider your file upload limits in LibreChat configuration

## Best Practices

### For Administrators

1. **Regular Permission Audits**: Review app permissions periodically
2. **Monitor Usage**: Track SharePoint integration usage in logs
3. **Update Documentation**: Keep internal docs updated with your tenant specifics
4. **Test Thoroughly**: Verify functionality after any Azure AD changes

### For End Users

1. **File Organization**: Well-organized SharePoint libraries improve user experience
2. **File Sizes**: Be mindful of large files that may slow conversations
3. **Permissions**: Ensure you have access to files before sharing
4. **Patient Downloads**: Allow time for multiple or large files

## Advanced Configuration

### Custom Scopes

For organizations with specific requirements, you can customize scopes:

```bash filename=".env"
# Example: Limiting to specific site collections
SHAREPOINT_PICKER_SHAREPOINT_SCOPE=https://contoso.sharepoint.com/sites/Engineering/AllSites.Read

# Example: Using more restrictive Graph permissions
SHAREPOINT_PICKER_GRAPH_SCOPE=Files.Read
```

### Integration with Information Barriers

If your organization uses Information Barriers:
- SharePoint integration respects all barrier policies
- Users only see content they're allowed to access
- No additional configuration required

## Related Documentation

- [Azure Entra Authentication](/docs/configuration/authentication/OAuth2-OIDC/azure)
- [OpenID Token Reuse](/docs/configuration/authentication/OAuth2-OIDC/token-reuse)
- [Microsoft Graph API Integration](/docs/configuration/authentication/OAuth2-OIDC/azure#advanced-microsoft-graph-api-integration)
- [File Upload Configuration](/docs/configuration/librechat_yaml/object_structure/file_config)


# Docker Override (https://www.librechat.ai/docs/configuration/docker_override)

A Docker Compose override file lets you change the default configuration in `docker-compose.yml` without editing or duplicating it. Override files are mainly for local customizations. When you run `docker compose up`, Compose merges `docker-compose.yml` with `docker-compose.override.yml` automatically.

<Callout type="info" title="More examples">
See `docker-compose.override.yml.example` in the repository for a fuller set of override snippets you can copy from.
</Callout>

## Configure the Override

<Steps>
<Step>

**Create the override file.** If you don't already have one, copy the example. Docker Compose picks it up automatically when you run `docker compose` commands.

```bash filename="terminal"
cp docker-compose.override.yml.example docker-compose.override.yml
```

</Step>
<Step>

**Edit the override file.** Open `docker-compose.override.yml` in your editor, then uncomment and customize the sections you need.

<Callout type="warning" title="One entry per service">
Each service name (`api`, `mongodb`, `meilisearch`, ...) can appear only once. To override multiple settings on a single service, combine them under that one entry.
</Callout>

</Step>
<Step>

**Apply the changes.** Run Docker Compose as usual. It merges `docker-compose.yml` and `docker-compose.override.yml` for you.

```bash filename="terminal"
docker compose up -d
```

</Step>
<Step>

**Verify the changes.** List the running containers and their properties, such as ports, to confirm your overrides took effect.

```bash filename="terminal"
docker ps
```

</Step>
</Steps>

## Examples

To mount your `librechat.yaml` config file so Docker can use it for [Custom Endpoints & Configuration](/docs/configuration/librechat_yaml):

```yaml filename="docker-compose.override.yml"
services:
  api:
    volumes:
      - ./librechat.yaml:/app/librechat.yaml
```

To build the `api` image locally, mount the config file, and use an older MongoDB that doesn't require AVX support:

```yaml filename="docker-compose.override.yml"
services:
  api:
    volumes:
      - ./librechat.yaml:/app/librechat.yaml
    image: librechat
    build:
      context: .
      target: node

  mongodb:
    image: mongo:4.4.18
```

<Callout type="warning" title="Watch exposed ports">
Exposing MongoDB or Meilisearch ports to the public can leave your data vulnerable. Avoid default ports for production or sensitive environments.
</Callout>

## Using `deploy-compose.yml`

With a non-default Compose file such as `deploy-compose.yml`, the override is not loaded automatically. Pass both files explicitly with `-f` (or `--file`); settings in later files override or add to earlier ones.

The override file can have any name, though you may already have `docker-compose.override.yml` in place. Run commands like so:

```bash filename="terminal"
docker compose -f deploy-compose.yml -f docker-compose.override.yml pull
docker compose -f deploy-compose.yml -f docker-compose.override.yml up
```

## Reference

- **Order of precedence:** values in the override file take precedence over the same values in `docker-compose.yml`.
- **Security:** when customizing ports and exposing services publicly, be conscious of the security implications and avoid defaults for production.

For more detail, see the official Docker documentation:

- [Understanding multiple Compose files](https://docs.docker.com/compose/how-tos/multiple-compose-files/extends/)
- [Merge Compose files](https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/)
- [Specifying multiple Compose files](https://docs.docker.com/compose/reference/#specifying-multiple-compose-files)


# Automated Moderation (https://www.librechat.ai/docs/configuration/mod_system)

The Automated Moderation System uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.

In production, you should have Cloudflare or some other DDoS protection in place to really protect the server from excessive requests, but these changes will largely protect you from the single or several bad actors targeting your deployed instance for proxying.

**For further details, refer to the user guide provided here: [Automated Moderation](/docs/features/mod_system)**

## Setup

The following are all of the related env variables to make use of and configure the mod system. Note this is also found in the [/.env.example](https://github.com/danny-avila/LibreChat/blob/main/.env.example) file, to be set in your own `.env` file.

**Note:** currently, most of these values are configured through the .env file, but they may soon migrate to be exclusively configured from the [`librechat.yaml` config file](/docs/configuration/librechat_yaml/object_structure/config#ratelimits).

### Violation, Interval, Duration

<OptionTable
  options={[
    ['BAN_VIOLATIONS', 'boolean', 'Whether or not to enable banning users for violations (they will still be logged).','BAN_VIOLATIONS=true'],
    ['BAN_DURATION', 'integer', 'How long the user and associated IP are banned for (in milliseconds).','BAN_DURATION=1000 * 60 * 60 * 2'],
    ['BAN_INTERVAL', 'integer', 'The user will be banned every time their score reaches/crosses over the interval threshold.','BAN_INTERVAL=20'],
    ['VIOLATION_SCORE_TTL', 'integer', 'How long a violation score lives without new violations (in milliseconds). Each new violation restarts the countdown, so scores decay after a quiet period instead of accumulating forever. Set to 0 to never expire scores (legacy behavior).','VIOLATION_SCORE_TTL=1000 * 60 * 60'],
  ]}
/>

### The score for each violation

<OptionTable
  options={[    
    ['LOGIN_VIOLATION_SCORE', 'integer', 'Score for login violations.','LOGIN_VIOLATION_SCORE=1'],
    ['REGISTRATION_VIOLATION_SCORE', 'integer', 'Score for registration violations.','REGISTRATION_VIOLATION_SCORE=1'],
    ['CONCURRENT_VIOLATION_SCORE', 'integer', 'Score for concurrent violations.','CONCURRENT_VIOLATION_SCORE=1'],
    ['MESSAGE_VIOLATION_SCORE', 'integer', 'Score for message violations.','MESSAGE_VIOLATION_SCORE=1'],
    ['NON_BROWSER_VIOLATION_SCORE', 'integer', 'Score for non-browser violations.','NON_BROWSER_VIOLATION_SCORE=20'],
    ['IMPORT_VIOLATION_SCORE', 'integer', 'Score for import conversation violations.','IMPORT_VIOLATION_SCORE=1'],
    ['FORK_VIOLATION_SCORE', 'integer', 'Score for conversation fork violations.','FORK_VIOLATION_SCORE=1'],
    ['TTS_VIOLATION_SCORE', 'integer', 'Score for text-to-speech violations.','TTS_VIOLATION_SCORE=0'],
    ['STT_VIOLATION_SCORE', 'integer', 'Score for speech-to-text violations.','STT_VIOLATION_SCORE=0'],
    ['FILE_UPLOAD_VIOLATION_SCORE', 'integer', 'Score for file upload violations.','FILE_UPLOAD_VIOLATION_SCORE=0'],
    ['RESET_PASSWORD_VIOLATION_SCORE', 'integer', 'Score for password reset violations.','RESET_PASSWORD_VIOLATION_SCORE=0'],
    ['VERIFY_EMAIL_VIOLATION_SCORE', 'integer', 'Score for email verification violations.','VERIFY_EMAIL_VIOLATION_SCORE=0'],
    ['TOOL_CALL_VIOLATION_SCORE', 'integer', 'Score for tool call violations.','TOOL_CALL_VIOLATION_SCORE=0'],
    ['CONVO_ACCESS_VIOLATION_SCORE', 'integer', 'Score for conversation access violations.','CONVO_ACCESS_VIOLATION_SCORE=0'],
  ]}
/>

### Login and registration rate limiting.

<OptionTable
  options={[    
    ['LOGIN_MAX', 'number', 'The max amount of logins allowed per IP per LOGIN_WINDOW. Defaults to `7`.'],
    ['LOGIN_WINDOW', 'number', 'In minutes, determines the window of time for LOGIN_MAX logins. Defaults to `5`.'],
    ['REGISTER_MAX', 'number', 'The max amount of registrations allowed per IP per REGISTER_WINDOW. Defaults to `5`.'],
    ['REGISTER_WINDOW', 'number', 'In minutes, determines the window of time for REGISTER_MAX registrations. Defaults to `60`.'],
  ]}
/>

### Message rate limiting

<OptionTable
  options={[    
    ['LIMIT_CONCURRENT_MESSAGES', 'boolean', 'Whether to limit the amount of messages a user can send per request.','LIMIT_CONCURRENT_MESSAGES=true'],
    ['CONCURRENT_MESSAGE_MAX', 'integer', 'The max amount of messages a user can send per request.','CONCURRENT_MESSAGE_MAX=2'],
  ]}
/>

> Note: You can utilize both limiters, but default is to limit by IP only.

#### Message rate limiting (per IP)

<OptionTable
  options={[    
    ['LIMIT_MESSAGE_IP', 'boolean', 'Whether to limit the amount of messages an IP can send per `MESSAGE_IP_WINDOW`.','LIMIT_MESSAGE_IP=true'],
    ['MESSAGE_IP_MAX', 'integer', 'The max amount of messages an IP can send per `MESSAGE_IP_WINDOW`.','MESSAGE_IP_MAX=40'],
    ['MESSAGE_IP_WINDOW', 'integer', 'In minutes, determines the window of time for `MESSAGE_IP_MAX` messages.','MESSAGE_IP_WINDOW=1'],
  ]}
/>

#### Message rate limiting (per User)
<OptionTable
  options={[    
    ['LIMIT_MESSAGE_USER', 'boolean', 'Whether to limit the amount of messages an user can send per `MESSAGE_USER_WINDOW`.','LIMIT_MESSAGE_USER=false'],
    ['MESSAGE_USER_MAX', 'integer', 'The max amount of messages an user can send per `MESSAGE_USER_WINDOW`.','MESSAGE_USER_MAX=40'],
    ['MESSAGE_USER_WINDOW', 'integer', 'In minutes, determines the window of time for `MESSAGE_USER_MAX` messages.','MESSAGE_USER_WINDOW=1'],
  ]}
/>

### Import conversation rate limiting

Limits how often users can import conversations to prevent abuse.

> Note: You can utilize both limiters, but default is to limit by IP only.

#### Import conversation rate limiting (per IP)

<OptionTable
  options={[    
    ['LIMIT_IMPORT_IP', 'boolean', 'Whether to limit the amount of conversation imports an IP can perform per `IMPORT_IP_WINDOW`.','LIMIT_IMPORT_IP=true'],
    ['IMPORT_IP_MAX', 'integer', 'The max amount of conversation imports an IP can perform per `IMPORT_IP_WINDOW`.','IMPORT_IP_MAX=100'],
    ['IMPORT_IP_WINDOW', 'integer', 'In minutes, determines the window of time for `IMPORT_IP_MAX` imports.','IMPORT_IP_WINDOW=1'],
  ]}
/>

#### Import conversation rate limiting (per User)

<OptionTable
  options={[    
    ['LIMIT_IMPORT_USER', 'boolean', 'Whether to limit the amount of conversation imports a user can perform per `IMPORT_USER_WINDOW`.','LIMIT_IMPORT_USER=false'],
    ['IMPORT_USER_MAX', 'integer', 'The max amount of conversation imports a user can perform per `IMPORT_USER_WINDOW`.','IMPORT_USER_MAX=50'],
    ['IMPORT_USER_WINDOW', 'integer', 'In minutes, determines the window of time for `IMPORT_USER_MAX` imports.','IMPORT_USER_WINDOW=1'],
  ]}
/>

### Conversation forking rate limiting

Limits how often users can fork conversations to prevent abuse.

> Note: You can utilize both limiters, but default is to limit by IP only.

#### Conversation forking rate limiting (per IP)

<OptionTable
  options={[    
    ['LIMIT_FORK_IP', 'boolean', 'Whether to limit the amount of conversation forks an IP can create per `FORK_IP_WINDOW`.','LIMIT_FORK_IP=true'],
    ['FORK_IP_MAX', 'integer', 'The max amount of conversation forks an IP can create per `FORK_IP_WINDOW`.','FORK_IP_MAX=30'],
    ['FORK_IP_WINDOW', 'integer', 'In minutes, determines the window of time for `FORK_IP_MAX` forks.','FORK_IP_WINDOW=1'],
  ]}
/>

#### Conversation forking rate limiting (per User)

<OptionTable
  options={[    
    ['LIMIT_FORK_USER', 'boolean', 'Whether to limit the amount of conversation forks a user can create per `FORK_USER_WINDOW`.','LIMIT_FORK_USER=false'],
    ['FORK_USER_MAX', 'integer', 'The max amount of conversation forks a user can create per `FORK_USER_WINDOW`.','FORK_USER_MAX=7'],
    ['FORK_USER_WINDOW', 'integer', 'In minutes, determines the window of time for `FORK_USER_MAX` forks.','FORK_USER_WINDOW=1'],
  ]}
/>

#### Illegal model requests

> Note: Illegal model requests are almost always nefarious as it means a 3rd party is attempting to access the server through an automated script. For this, I recommend a relatively high score, no less than 5.

<OptionTable
  options={[    
    ['ILLEGAL_MODEL_REQ_SCORE', 'integer', 'Score for illegal model requests.','ILLEGAL_MODEL_REQ_SCORE=5'],
  ]}
/>

## OpenAI text moderation

<OptionTable
  options={[    
    ['OPENAI_MODERATION', 'boolean', 'Whether or not to enable OpenAI moderation on the **OpenAI** and **Plugins** endpoints.','OPENAI_MODERATION=false'],
    ['OPENAI_MODERATION_API_KEY', 'string', 'Your OpenAI API key.','OPENAI_MODERATION_API_KEY='],
  ]}
/>


Note that this might not work with all reverse proxies:

<OptionTable
  options={[    
    ['OPENAI_MODERATION_REVERSE_PROXY', 'string', 'Note: Commented out by default, this is not working with all reverse proxys.','# OPENAI_MODERATION_REVERSE_PROXY='],
  ]}
/>


# Langfuse Tracing (https://www.librechat.ai/docs/configuration/langfuse)

[Langfuse](https://langfuse.com) is an open-source LLM observability platform that helps you trace, monitor, and debug your LLM applications. By integrating Langfuse with LibreChat, you get full visibility into your AI conversations.

## Prerequisites

Before you begin, ensure you have:

1. A running LibreChat instance (see [Quick Start](/docs/quick_start))
2. A Langfuse account ([sign up for free](https://cloud.langfuse.com))
3. Langfuse API keys from your project settings

## Setup

Choose either an in-app connection or environment-managed credentials for a single-tenant deployment. Complete environment credentials take precedence.

### In-App Connection

An authorized administrator can open **Settings → Langfuse**, choose an approved destination, enter the project's public and secret keys, and select **Save & enable**. LibreChat verifies both keys before saving, records the verified project ID, encrypts the secret key at rest, and returns only a masked preview on later reads. A saved connection can be enabled, disabled, retested, or replaced without exposing its secret.

The setting requires both `access:admin` and `manage:configs:langfuse` (or the corresponding broad/admin permissions). It is available when tracing is enabled and:

- A single-tenant deployment does not have both `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY`.
- A fanout deployment has an enabled collector and tenant export is not disabled by the emergency switch.

Do not place a plaintext `langfuse.secretKey` directly in `librechat.yaml`; the runtime accepts the encrypted value produced by an authorized config write. Use the Settings page for the normal connection workflow.

The saved connection belongs to the base configuration. Role, group, and user configuration overrides cannot replace or remove the `langfuse` section.

### Environment-Managed Connection

Add the following values to your `.env` file when deployment operators should own the central connection:

<OptionTable
  options={[
    ['LANGFUSE_PUBLIC_KEY', 'string', 'Your Langfuse public key.', 'LANGFUSE_PUBLIC_KEY=pk-lf-***'],
    ['LANGFUSE_SECRET_KEY', 'string', 'Your Langfuse secret key.', 'LANGFUSE_SECRET_KEY=sk-lf-***'],
    [
      'LANGFUSE_BASE_URL',
      'string',
      'The Langfuse API base URL.',
      'LANGFUSE_BASE_URL=https://cloud.langfuse.com',
    ],
    [
      'LANGFUSE_PROJECT_ID',
      'string',
      'Optional stable project ID. When omitted, LibreChat discovers and caches it in the background for feedback routing.',
      'LANGFUSE_PROJECT_ID=',
    ],
    [
      'LANGFUSE_TRACING_ENABLED',
      'boolean',
      'Set to false to disable Langfuse traces and feedback scores. Default: true.',
      'LANGFUSE_TRACING_ENABLED=true',
    ],
    [
      'LANGFUSE_SAMPLE_RATE',
      'number',
      'Deterministic trace-level sample rate from 0 to 1. Default: 1.',
      'LANGFUSE_SAMPLE_RATE=1',
    ],
  ]}
/>

### Example Configuration

```sh filename=".env"
# Langfuse Configuration
LANGFUSE_PUBLIC_KEY=pk-lf-***
LANGFUSE_SECRET_KEY=sk-lf-***
# LANGFUSE_PROJECT_ID=project-id
# LANGFUSE_SAMPLE_RATE=1

# 🇪🇺 EU Data Region
LANGFUSE_BASE_URL=https://cloud.langfuse.com

# 🇺🇸 US Data Region
# LANGFUSE_BASE_URL=https://us.cloud.langfuse.com
```

<Callout type="note" title="Self-Hosted Langfuse">
  For self-hosted Langfuse instances, set `LANGFUSE_BASE_URL` to your custom URL (e.g.,
  `http://localhost:3000` for local development).
</Callout>

When both environment keys are present, LibreChat uses them for the central project and hides **Settings → Langfuse**. `LANGFUSE_TRACING_ENABLED=false` or `LANGFUSE_SAMPLE_RATE=0` disables both traces and feedback scores. Fractional sampling makes one deterministic decision per trace; sampled-out traces do not receive later feedback scores.

`LANGFUSE_BASE_URL` is the canonical base-URL setting. The older `LANGFUSE_HOST` and `LANGFUSE_BASEURL` names remain compatibility aliases, with `LANGFUSE_BASE_URL` taking precedence when more than one is set.

### Authenticated Proxies and Gateways

For a self-hosted Langfuse instance behind Cloudflare Access, oauth2-proxy, or another authenticating gateway, define deployment-level custom request headers in `librechat.yaml`:

```yaml filename="librechat.yaml"
langfuse:
  headers:
    CF-Access-Client-Id: '${CF_ACCESS_CLIENT_ID}'
    CF-Access-Client-Secret: '${CF_ACCESS_CLIENT_SECRET}'
```

LibreChat applies the resolved headers to every direct Langfuse surface: trace and media export, feedback-score creation and deletion, project-identity lookup, and admin credential verification. Langfuse's own `Authorization` credential remains authoritative on REST requests. Values support `${ENV_VAR}` interpolation; unresolved variables, protected infrastructure-secret references, blank values, and invalid header names are dropped with a warning. Header values are masked in startup logs and admin configuration reads, but environment references are still recommended over literal credentials.

These headers are operator-owned and YAML-only. They cannot be written through the Admin Panel or configuration API, stored in MongoDB, or populated with per-user `{{...}}` placeholders. LibreChat sends them only when configuration resolves exactly one Langfuse origin. With multiple central, tenant, or collector origins, it logs a warning and sends no custom headers because the map cannot safely select a recipient.

<Callout type="warning" title="Fanout limitation">
  The fanout collector currently forwards only `Authorization` to tenant Langfuse destinations.
  Custom `langfuse.headers` can authenticate LibreChat's direct request to a single collector or
  Langfuse origin, but they do not authenticate the collector's upstream request to a tenant
  destination behind another proxy.
</Callout>

## Tenant Fanout (Optional)

Langfuse fanout lets a multi-tenant LibreChat deployment export each eligible trace and its media to both a central Langfuse project and a tenant-specific project. The feature is opt-in and adds a gateway plus an internal OpenTelemetry collector; normal single-project tracing above is unchanged when fanout is not deployed.

Fanout uses two configuration layers:

- Deployment environment variables define the central project, gateway, and allowed tenant destinations at startup.
- **Settings → Langfuse** supplies the tenant's enabled state, public key, encrypted secret key, verified project ID, and one allowed destination. Keys can be changed at runtime without restarting the gateway.

The included Compose override supports the `eu`, `us`, and `jp` Langfuse Cloud destinations:

```sh filename=".env"
LANGFUSE_BASE_URL=https://cloud.langfuse.com
LANGFUSE_FANOUT_CENTRAL_BASE_URL=https://cloud.langfuse.com
LANGFUSE_FANOUT_CENTRAL_AUTH_HEADER="Basic <base64-public-key-colon-secret-key>"
LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED=false
LANGFUSE_FANOUT_TENANT_DESTINATIONS="eu=https://cloud.langfuse.com,us=https://us.cloud.langfuse.com,jp=https://jp.cloud.langfuse.com"
LANGFUSE_FANOUT_TRACE_DESTINATION_KEYS=eu,us,jp
LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED=false
```

Start the regular Compose stack with the fanout override:

```sh
docker compose -f docker-compose.yml -f docker-compose.langfuse-fanout.yml up -d
```

The override enables `LANGFUSE_FANOUT_ENABLED`, points LibreChat at the gateway, and starts the collector and private Redis services. For the deployed stack, combine `deploy-compose.yml` with `deploy-compose.langfuse-fanout.yml` instead.

The gateway listens on `:4318` by default. Custom deployments can override its bind address with `LANGFUSE_FANOUT_LISTEN_ADDR`; the included Compose and Helm configurations already route the standard gateway port.

### Helm Deployment

The Compose overrides build the gateway locally, but Kubernetes needs an image available from a registry. From the LibreChat repository root, build and push the gateway before installing the chart:

```sh
docker build \
  -f otel/langfuse-fanout/Dockerfile \
  -t registry.example.com/librechat-langfuse-fanout:<tag> .
docker push registry.example.com/librechat-langfuse-fanout:<tag>
```

Create a Kubernetes Secret containing the full central Langfuse Basic auth header. Its value is `Basic ` followed by base64-encoded `<public-key>:<secret-key>` credentials:

```sh
kubectl create secret generic langfuse-central \
  --from-literal=LANGFUSE_FANOUT_CENTRAL_AUTH_HEADER='Basic <base64-public-key-colon-secret-key>'
```

Enable the fanout deployment in Helm values. This example uses the bundled Redis chart; set `langfuseFanout.redis.uri` instead when using an external Redis service.

```yaml filename="values.yaml"
redis:
  enabled: true

langfuseFanout:
  enabled: true
  image:
    repository: registry.example.com/librechat-langfuse-fanout
    tag: '<tag>'
    pullPolicy: IfNotPresent
  central:
    baseUrl: https://cloud.langfuse.com
    authHeaderSecret:
      name: langfuse-central
      key: LANGFUSE_FANOUT_CENTRAL_AUTH_HEADER
  metrics:
    secret:
      name: librechat-metrics
      key: METRICS_SECRET
  tenant:
    destinations:
      eu:
        baseUrl: https://cloud.langfuse.com
      us:
        baseUrl: https://us.cloud.langfuse.com
      jp:
        baseUrl: https://jp.cloud.langfuse.com
  upstreamTimeout: 30s
  publicUrl: ''
  otelCollector:
    receiverEndpoint: 127.0.0.1:4319
  redis:
    uri: ''
    username: ''
    passwordSecret:
      name: ''
      key: REDIS_PASSWORD
    keyPrefix: langfuse-fanout
  memoryLimitMiB: 256
  memorySpikeLimitMiB: 64
  batchTimeout: 1s
  batchSendSize: 128
  metadataCardinalityLimit: 1000
```

The metrics Secret is optional, but `/metrics` returns `401` unless `langfuseFanout.metrics.secret` supplies a bearer token. Create it separately when metrics scraping is required:

```sh
kubectl create secret generic librechat-metrics \
  --from-literal=METRICS_SECRET='<metrics-bearer-token>'
```

The chart renders one Deployment with a gateway container on port `4318` and an internal OpenTelemetry Collector sidecar on port `4319`; only the gateway is exposed by the Service. It also injects `LANGFUSE_FANOUT_ENABLED` and the internal `LANGFUSE_FANOUT_COLLECTOR_URL` into LibreChat unless those keys are already set in `librechat.configEnv`.

Redis stores short-lived, one-time media upload plans so create and upload requests can reach different gateway replicas. For an external authenticated Redis service, set `langfuseFanout.redis.uri`, optional `username`, and `passwordSecret`; keep credentials out of the URI because the URI is rendered directly into the Deployment environment. With the bundled Redis chart and authentication enabled, provide a compatible password Secret or an explicitly authenticated external URI.

Scale fanout manually with `langfuseFanout.replicaCount`; the chart does not create a fanout HPA. Liveness and readiness probes use `/healthz` and can be customized under `langfuseFanout.livenessProbe` and `langfuseFanout.readinessProbe`.

Additional deployment overrides include `langfuseFanout.service` for the Service type, port, and annotations; `resources`, `podAnnotations`, and `podLabels` for the gateway Pod; and `otelCollector.image` and `otelCollector.resources` for the sidecar. If you change `langfuseFanout.traceCollectorUrl` or `otelCollector.receiverEndpoint`, keep both values pointed at the same internal collector listener.

The stored top-level `langfuse` object contains `enabled`, `publicKey`, encrypted `secretKey`, verified `projectId`, server-generated `secretKeyPreview`, and `destination`. Treat it as API-managed configuration: do not submit preview fields or encrypted payloads back as secrets. The earlier `displaySecretKey` and nested `fanout.enabled` fields are no longer used.

Tenant export occurs only when fanout and the collector URL are enabled, the saved connection is enabled with valid keys, and `destination` matches an allowed startup destination. Other traces can still flow to the central project. Set `LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED=true` as an emergency switch to stop tenant trace and feedback-score export while retaining central export; this also hides the in-app connection setting until tenant export is restored.

Set `LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED=true` to prevent the LibreChat SDK from creating media uploads for central or fallback collector traces; tenant-routed media uploads are unchanged. This app-side switch is distinct from the gateway-side `LANGFUSE_FANOUT_CENTRAL_MEDIA_EXPORT_DISABLED`, which blocks central media forwarding after an upload reaches the gateway.

For media forwarding, the fanout gateway accepts only absolute HTTPS upload targets returned by the configured Langfuse authority and does not follow redirects on upload `PUT` requests. Self-hosted object storage, including private MinIO hosts, remains supported when its returned upload URL uses HTTPS. Plain HTTP or redirect-dependent media upload targets now fail closed while trace export continues independently.

Trace and media traffic goes through the fanout gateway. Feedback scores are sent directly from LibreChat: central scores use `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, and `LANGFUSE_BASE_URL`, while tenant scores use tenant app configuration.

See the [Langfuse fanout deployment reference](https://github.com/danny-avila/LibreChat/blob/dev/otel/langfuse-fanout/README.md) for every gateway variable, custom destination setup, metrics, scaling, and Redis behavior.

### Export Diagnostics

Each traced request records low-cardinality OpenTelemetry attributes that explain its tenant export decision:

- `librechat.tenant.id` identifies the tenant context
- `librechat.langfuse.export_plan` is `central_only`, `tenant_fanout`, or `disabled`
- `librechat.langfuse.export_reason` is `configured`, `collector_unconfigured`, `destination_unconfigured`, `emergency_disabled`, `fanout_disabled`, `missing_credentials`, or `tenant_disabled`

The gateway also exposes `langfuse_fanout_trace_exports_total{destination,result,tenant_id}`. A batch with no tenant uses `<unknown>`, a mixed-tenant batch uses `<multiple>`, and a malformed tenant ID uses `<invalid>`. The gateway retains at most 1,000 distinct valid tenant labels per process and combines later IDs under `<overflow>`, bounding metric cardinality; unrecognized destinations are recorded as `central` rather than creating another label.

Successful in-app connection changes also write the structured event `librechat.langfuse.connection.changed`. It includes the tenant, configured and enabled state, destination, verification result, primary change, and complete changed-field list without logging either key. Use these fields to distinguish an intentional central-only or disabled plan from missing collector, destination, or credential configuration.

## Restart LibreChat

After adding the environment variables, restart your LibreChat instance to apply the changes:

```sh
docker compose down
docker compose up -d
```

## See Traces in Langfuse

Once LibreChat is restarted with Langfuse configured, you will see a new trace for every chat message response in the Langfuse UI:

LibreChat v0.8.8-rc2 updates Agent trace shaping to use `StandardGraph`, `MultiAgentGraph`, and `AgentModelCall` runtime observation names, with activity and reasoning-label calls nested under the work they describe. Trace-level input and output are also represented on root observations. Review saved Langfuse filters, dashboards, and evaluations that depend on the earlier observation names or trace-level input/output fields.

![LibreChat example trace](https://langfuse.com/images/cookbook/integration_librechat/librechat-example-trace.png)

[Link to trace in the Langfuse UI](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/57b4aa20a258a2e9e2d1ce2e4eacb21c?observation=06f5341a68d723b1&timestamp=2026-02-04T13%3A02%3A54.248Z&traceId=57b4aa20a258a2e9e2d1ce2e4eacb21c)

Administrators with `access:admin` and `manage:configs:langfuse` can open the conversation's **Context Usage** breakdown and select **View session in Langfuse**. The same action can appear in a shared conversation for an authenticated, same-tenant administrator with Langfuse configuration access. Public and ordinary shared-link viewers never see it.

The link appears only after generation finishes and when the conversation contains at least one sampled trace sent to the active in-app Langfuse connection. It points to that connection's verified destination and project; it is not shown for environment-managed credentials, disabled connections, sampled-out conversations, cross-tenant viewers, or users without the required access.

## Message Feedback Scores

When Langfuse tracing is configured, LibreChat also sends message feedback to Langfuse as a `user-feedback` BOOLEAN score on the matching trace. A thumbs-up rating is sent as `1`, a thumbs-down rating is sent as `0`, and any selected feedback tag or comment is included on the score. Clearing feedback deletes the score.

Feedback scores include message context metadata when available, including the message ID, parent message ID, conversation/session ID, user ID, endpoint, sender, `isCreatedByUser`, token count, rating, and feedback tag. Empty metadata values are omitted before the score is sent.

Feedback scores are only produced when the feedback buttons are available. Setting [`interface.feedback`](/docs/configuration/librechat_yaml/object_structure/interface#feedback) to `false` hides the buttons and rejects feedback writes, so no scores reach Langfuse.

Feedback scores use the same Langfuse credentials and base URL as tracing. They also respect `LANGFUSE_TRACING_ENABLED=false`, `LANGFUSE_SAMPLE_RATE=0`, and `LANGFUSE_TRACING_ENVIRONMENT`. Score delivery is best-effort, so the feedback UI does not block if Langfuse is temporarily unavailable.


# Logging System (https://www.librechat.ai/docs/configuration/logging)

### General

LibreChat has central logging built into its backend (api).

- With the **docker** install, log files are saved in `/logs`

<FileTree>
  <FileTree.Folder name="librechat" defaultOpen>
    <FileTree.Folder name="logs" defaultOpen>
      <FileTree.File name="debug-2024-01-01.log" active />
      <FileTree.File name="error-2024-01-01.log" active />
      <FileTree.File name="meiliSync-2024-01-01.log" active />
    </FileTree.Folder>
  </FileTree.Folder>
</FileTree>

- With the **npm** install, log files are saved in `/api/logs`

<FileTree>
  <FileTree.Folder name="librechat" defaultOpen>
    <FileTree.Folder name="api" defaultOpen>
      <FileTree.Folder name="logs" defaultOpen>
        <FileTree.File name="debug-2024-01-01.log" active />
        <FileTree.File name="error-2024-01-01.log" active />
        <FileTree.File name="meiliSync-2024-01-01.log" active />
      </FileTree.Folder>
    </FileTree.Folder>
  </FileTree.Folder>
</FileTree>

Error logs are saved by default. Debug logs are enabled by default but can be turned off if not desired.

This allows you to monitor your server through external tools that inspect log files, such as **[the ELK stack](https://aws.amazon.com/what-is/elk-stack/)**.

Debug logs are essential for developer work and fixing issues. If you encounter any problems running LibreChat, reproduce as close as possible, and **[report the issue](https://github.com/danny-avila/LibreChat/issues)** with your logs found in `./api/logs/debug-%DATE%.log`.

Errors logs are also saved in the same location: `./api/logs/error-%DATE%.log`. If you have meilisearch configured, there is a separate log file for this as well.

<Callout type="note" title="Note:">
  Note: Logs are rotated on a 14-day basis, so you will generate one error log file, one debug log
  file, and one meiliSync log file per 14 days. Errors will also be present in debug log files as
  well, but provide stack traces and more detail in the error log files.
</Callout>

### Setup

- Toggle debug logs with the following environment variable. By default, even if you never set this variable, debug logs will be generated, but you have the option to disable them by setting it to `FALSE`.

<OptionTable
  options={[['DEBUG_LOGGING', 'boolean', 'Keep debug logs active.', 'DEBUG_LOGGING=true']]}
/>

> Note: it's recommended to disable debug logs in a production environment.

- For verbose server output in the console/terminal, you can set the following to `TRUE`:

<OptionTable
  options={[
    [
      'DEBUG_CONSOLE',
      'boolean',
      'Enable verbose console/stdout logs in the same format as file debug logs.',
      'DEBUG_CONSOLE=false',
    ],
  ]}
/>

This is not recommended however, as the outputs can be quite verbose. It's disabled by default and should be enabled sparingly.

- When handling console logs in cloud deployments (such as GCP or AWS), enabling this will dump the logs with a UTC timestamp and format them as JSON.

<OptionTable
  options={[
    [
      'CONSOLE_JSON',
      'boolean',
      'Enable verbose JSON console/stdout logs suitable for cloud deployments like GCP/AWS.',
      'CONSOLE_JSON=false',
    ],
  ]}
/>

By default, the JSON string length is truncated to 255 characters. You can configure this with the following environment variable:

<OptionTable
  options={[
    [
      'CONSOLE_JSON_STRING_LENGTH',
      'number',
      'Configure the truncation size for string values in JSON console/stdout logs. Default: 255.',
      '# CONSOLE_JSON_STRING_LENGTH=255',
    ],
  ]}
/>

- File-backed log transports are enabled by default. Set `LOG_TO_FILE=false` if your deployment should only emit logs to stdout/stderr.

<OptionTable
  options={[
    [
      'LOG_TO_FILE',
      'boolean',
      'Set to false to disable file-backed Winston transports.',
      'LOG_TO_FILE=true',
    ],
  ]}
/>

### OpenTelemetry Tracing

LibreChat can emit backend OpenTelemetry traces for server, database, Redis, and outbound HTTP visibility. Redis command-level spans are opt-in so default traces stay high-level. This is separate from Langfuse, which remains the recommended option for GenAI-specific prompt and model observability.

<OptionTable
  options={[
    [
      'OTEL_TRACING_ENABLED',
      'boolean',
      'Enable backend OpenTelemetry tracing.',
      '# OTEL_TRACING_ENABLED=false',
    ],
    [
      'OTEL_SERVICE_NAME',
      'string',
      'Service name reported to OpenTelemetry. Default: librechat.',
      '# OTEL_SERVICE_NAME=librechat',
    ],
    [
      'OTEL_SERVICE_VERSION',
      'string',
      'Service version reported to OpenTelemetry.',
      '# OTEL_SERVICE_VERSION=',
    ],
    [
      'OTEL_EXPORTER_OTLP_ENDPOINT',
      'string',
      'Base OTLP exporter endpoint.',
      '# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318',
    ],
    [
      'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT',
      'string',
      'Trace-specific OTLP endpoint.',
      '# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=',
    ],
    [
      'OTEL_EXPORTER_OTLP_HEADERS',
      'string',
      'OTLP exporter headers.',
      '# OTEL_EXPORTER_OTLP_HEADERS=',
    ],
    ['OTEL_TRACES_EXPORTER', 'string', 'Trace exporter selection.', '# OTEL_TRACES_EXPORTER=otlp'],
    [
      'OTEL_TRACES_SAMPLER',
      'string',
      'OpenTelemetry trace sampler.',
      '# OTEL_TRACES_SAMPLER=parentbased_always_on',
    ],
    ['OTEL_LOG_LEVEL', 'string', 'OpenTelemetry SDK log level.', '# OTEL_LOG_LEVEL=INFO'],
    [
      'OTEL_SDK_DISABLED',
      'boolean',
      'Disable the OpenTelemetry SDK even when tracing is enabled.',
      '# OTEL_SDK_DISABLED=false',
    ],
    [
      'OTEL_IOREDIS_TRACING_ENABLED',
      'boolean',
      'Enable Redis command-level spans. Disabled by default to keep backend traces high-level.',
      '# OTEL_IOREDIS_TRACING_ENABLED=false',
    ],
  ]}
/>


# Metrics (https://www.librechat.ai/docs/configuration/metrics)

## General

![Active users in LibreChat](/images/metrics/librechat-metrics-active-users.png)

LibreChat provides two Prometheus-compatible metrics surfaces. The API's built-in `/metrics` endpoint reports operational behavior such as Redis activity and browser telemetry proxy outcomes. The optional database exporter reports usage data stored in MongoDB, including token totals and active-user counts.

## Built-in API Metrics

LibreChat exposes Prometheus metrics from the API server at `/metrics`. Set `METRICS_SECRET` and scrape the endpoint with `Authorization: Bearer <METRICS_SECRET>`. The endpoint returns `401` when the secret is unset, the header is missing, or the token does not match.

```yaml filename="prometheus.yml"
scrape_configs:
  - job_name: librechat-api
    scheme: https
    metrics_path: /metrics
    authorization:
      type: Bearer
      credentials: your-metrics-secret
    static_configs:
      - targets: ['librechat.example.com']
```

### Agent Startup Metrics

Initial Agent chat requests emit two startup metrics:

- `agent_startup_milestone_duration_seconds{milestone}` measures cumulative latency from request ingress to each startup milestone.
- `agent_startups_total{result}` counts startup attempts by terminal result.

Milestones cover request admission, job creation and acknowledgement, conversation and history loading, client and run initialization, stream startup, and the first queued response or content event. Results distinguish content queued, completion without a content delta, deduplication, rejection, pause, replacement, abort, and error.

When [backend OpenTelemetry tracing](/docs/configuration/dotenv#opentelemetry-tracing) is enabled, the `librechat.agent.startup` span reports the same milestones as events. It includes total startup duration, milestone count, terminal result, and the generation stream ID when one is assigned.

### Redis Metrics

Redis-backed caches and services emit two logical-operation metrics:

- `redis_operations_total{client,use_case,operation,status}` counts operations.
- `redis_operation_duration_seconds{client,use_case,operation,status}` measures latency.

`client` is `keyv` or `ioredis`, and `status` is `success` or `error`. Bounded `use_case` labels identify work such as caches, sessions, rate limits, concurrency, ACL principals, resumable stream jobs and pub/sub, MCP registry scans, and leader election. These metrics count LibreChat's logical operations rather than every internal Redis round trip. Connection lifecycle pings and global maintenance are excluded, and clearing a namespace is counted as one logical operation.

When [backend OpenTelemetry tracing](/docs/configuration/dotenv#opentelemetry-tracing) is enabled, each HTTP span also summarizes its Redis work with:

- `librechat.redis.calls`
- `librechat.redis.duration_ms`
- `librechat.redis.errors`
- `librechat.redis.max_call_ms`
- `librechat.redis.operations`
- `librechat.redis.use_cases`

The ten use cases with the highest total duration also receive `calls`, `duration_ms`, `errors`, and `max_call_ms` attributes under `librechat.redis.<use_case>.*`. This request-level summary does not require command-level Redis spans; enable `OTEL_IOREDIS_TRACING_ENABLED` only when individual command spans are needed.

### Browser RUM Proxy Metrics

Browser RUM proxy outcomes are reported as `rum_proxy_requests_total{endpoint,result}`. `endpoint` is `traces`, `logs`, or `unknown`; `result` can be `success`, `auth_drop`, `auth_error`, `bad_request`, `not_configured`, `collector_4xx`, `collector_5xx`, `collector_error`, or `collector_timeout`.

### Agent Event Actor Metrics

Durable bound Agent Events expose low-cardinality receipt and recovery metrics:

- `agent_event_actor_receipt_operations_total{operation,outcome,resolution}` counts receipt reads, settlement, and legacy backfill. `operation` is `read`, `settle`, or `backfill`; `outcome` is `hit`, `miss`, `success`, `replay`, or `conflict`.
- `agent_event_actor_receipts_retained{resolution}` reports replay receipts retained by `checkpoint_verified`, `action_compensated`, or `history_repaired` resolution.
- `agent_event_actor_receipts_expiry_eligible` reports retained receipts whose 90-day MongoDB TTL has elapsed but which have not yet been removed.
- `agent_event_actor_reconciliations_pending` reports active reconciliation markers waiting for a terminal receipt.
- `agent_event_actor_oldest_reconciliation_age_seconds` reports the age of the oldest active reconciliation.
- `agent_event_actor_deliveries{state}` reports delivery rows currently in `retry` or `dead` state.

Storage gauges are collected on authenticated scrapes and cached for up to 60 seconds. Alert on sustained reconciliation age, dead deliveries, or expiry-eligible receipts rather than a single scrape.

### Shared-Link Metrics

`share_link_rejections_total{operation,code}` counts bounded create or update failures. `operation` is `create` or `update`; `code` is `TARGET_MESSAGE_NOT_FOUND` when the selected branch tail is not persisted, or `NO_MESSAGES` when the conversation has no persisted messages. The metric contains no conversation text or user identity.

## Database Metrics Exporter

The metrics exporter is available at [virtUOS/librechat_exporter](https://github.com/virtUOS/librechat_exporter).
It is a separate tool you deploy alongside LibreChat.

### Setup

To deploy the exporter, just add the necessary container to your compose configuration like this:

```yaml
services:
  metrics:
    image: ghcr.io/virtuos/librechat_exporter:main
    depends_on:
      - mongodb
    ports:
      - '8000:8000'
    restart: unless-stopped
```

You can optionally also configure the exporter.
But usually, the defaults should be just fine.

```yaml
services:
  metrics:
    environment:
      - MONGODB_URI=mongodb://mongodb:27017/
      - LOGGING_LEVEL=info
```

### Usage

You can now add the exporter to your Prometheus scrape configuration:

```yaml
- job_name: librechat
  static_configs:
    - targets:
        - 'librechat.example.com:8000'
```

Once scraping the metrics has started, look for `librechat_*` metrics (e.g., `librechat_registered_users`).
The exporter provides several metrics.

Have fun building your Grafana dashboard!


# Meilisearch (https://www.librechat.ai/docs/configuration/meilisearch)

Meilisearch is an open-source search engine that powers LibreChat's conversation search, adding full-text search, typo tolerance, and instant results across past conversations. For a feature overview, see [Search in LibreChat](/docs/features/search).

<Callout type="info" title="How it connects">
LibreChat talks to Meilisearch over HTTP using a few environment variables. The Docker setup ships Meilisearch as a service for you. A source install points LibreChat at a Meilisearch process you run yourself.
</Callout>

## Configure Meilisearch

<Tabs items={['Docker', 'npm']}>
<Tabs.Tab value="Docker">

The default `docker-compose.yml` already includes a `meilisearch` service, so you only need to enable search and set a master key in your `.env` file.

<Steps>
<Step>

**Generate a master key.** Use any sufficiently long, random string (16 bytes or more). For example:

```bash filename="terminal"
openssl rand -base64 32
```

</Step>
<Step>

**Add the search variables to `.env`.** The Compose file sets `MEILI_HOST` to the internal service address for the `api` container, so you don't set the host here. Keep the master key identical to the one the `meilisearch` service uses.

```bash filename=".env"
SEARCH=true
MEILI_NO_ANALYTICS=true
MEILI_MASTER_KEY=<your_master_key>
```

</Step>
<Step>

**Pass the master key to the Meilisearch service.** The bundled `meilisearch` service does not read `.env`, so add it through `docker-compose.override.yml`. This keeps both LibreChat and Meilisearch using the same key.

```yaml filename="docker-compose.override.yml"
services:
  meilisearch:
    environment:
      - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
```

See [Docker Override](/docs/configuration/docker_override) for how override files are merged.

</Step>
<Step>

**Start the stack.** Compose merges the override automatically and starts Meilisearch alongside LibreChat.

```bash filename="terminal"
docker compose up -d
```

</Step>
</Steps>

<Callout type="warning" title="Keep the port internal">
Containers reach Meilisearch over the internal Docker network, so there is no need to publish port `7700` to the host. Exposing it publicly can leave your search data vulnerable.
</Callout>

</Tabs.Tab>
<Tabs.Tab value="npm">

When you run LibreChat from source, run the Meilisearch binary as a separate process and point LibreChat at it.

<Steps>
<Step>

**Download Meilisearch.** Get the latest release for your operating system from the [Meilisearch releases page](https://github.com/meilisearch/meilisearch/releases), for example `meilisearch-linux-amd64.tar.gz` (Linux), `meilisearch-macos-amd64` (macOS), or `meilisearch-windows-amd64.zip` (Windows). Extract it to a directory of your choice. For package-manager installs, see the [Meilisearch installation guide](https://www.meilisearch.com/docs/learn/getting_started/installation).

</Step>
<Step>

**Make the binary executable (Linux/macOS).** From the directory where you extracted it:

```bash filename="terminal"
chmod +x meilisearch
```

</Step>
<Step>

**Generate a master key.** Meilisearch can generate one for you:

```bash filename="terminal"
./meilisearch --generate-master-key
```

Copy the generated key; you reuse it in the next steps.

</Step>
<Step>

**Start Meilisearch.** Run it with your master key. It listens on port `7700` by default.

```bash filename="terminal"
./meilisearch --master-key=<your_master_key>
```

</Step>
<Step>

**Add the search variables to `.env`.** Point `MEILI_HOST` at the Meilisearch process and use the same master key you set above.

```bash filename=".env"
SEARCH=true
MEILI_NO_ANALYTICS=true
MEILI_HOST=http://localhost:7700
MEILI_MASTER_KEY=<your_master_key>
```

</Step>
<Step>

**Start LibreChat.** Start or restart the app so it picks up the new configuration.

```bash filename="terminal"
npm run backend
```

</Step>
</Steps>

<Callout type="info" title="Keep Meilisearch running">
Conversation search only works while Meilisearch is running. Run it as a managed service or container so it stays up across restarts.
</Callout>

</Tabs.Tab>
</Tabs>

Once configured, LibreChat indexes conversations and messages into Meilisearch, and the search bar returns full-text results with typo tolerance.

### Reindexing after v0.8.8-rc2

LibreChat v0.8.8-rc2 adds an internal indexed-projection version to conversations and messages. On the first synchronization after upgrading, existing documents without the current marker are treated as stale and reindexed automatically so newer searchable fields, including message-backed sidebar results, are present in Meilisearch.

No manual reset is required for this migration. Large installations may see temporarily elevated MongoDB, Meilisearch, and indexing-worker load while the stale documents are processed. In a multi-node deployment, keep synchronization enabled on only one LibreChat node as described below.

## Environment Variables

| Variable | Description |
| --- | --- |
| `SEARCH` | Enables the conversation search feature. Set to `true`. |
| `MEILI_HOST` | URL where LibreChat reaches Meilisearch. In Docker this is `http://meilisearch:7700` (set by Compose); from source it is typically `http://localhost:7700`. |
| `MEILI_MASTER_KEY` | Shared secret used to authenticate with Meilisearch. Must match the key Meilisearch starts with. |
| `MEILI_NO_ANALYTICS` | Disables Meilisearch's anonymous telemetry. Set to `true`. |
| `MEILI_NO_SYNC` | See [multi-node setups](#disable-sync-in-a-multi-node-setup). |

## Disable Sync in a Multi-node Setup

If you run LibreChat as a node cluster or multi-node deployment, leave synchronization enabled on one instance and set `MEILI_NO_SYNC=true` on every other instance. This prevents redundant indexing work across nodes.

```bash filename=".env"
MEILI_NO_SYNC=true
```

## Reset Synchronization

If Meilisearch data is deleted or corrupted, or LibreChat treats everything as synced when it isn't (for example after upgrading Meilisearch or deleting its data files), use the reset script to force a full re-sync. It resets the synchronization flags in MongoDB, which triggers LibreChat to re-index all conversations and messages on the next startup or sync check.

<Steps>
<Step>

**Run the reset script.** Use the command that matches your setup.

```bash filename="terminal"
# Local development
npm run reset-meili-sync

# Docker (default setup)
docker compose exec api npm run reset-meili-sync

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run reset-meili-sync"
```

</Step>
<Step>

**Restart LibreChat.** Re-synchronization begins once the app restarts.

</Step>
</Steps>

The script resets the `_meiliIndex` flag to `false` for all messages and conversations in MongoDB, then reports how many documents were reset and how many remain to be synced.

**When to use it:**

- After deleting Meilisearch data files
- When upgrading Meilisearch to a version that requires reindexing
- When LibreChat shows conversations as fully synced but Meilisearch is missing data
- After restoring a MongoDB backup without matching Meilisearch data

**Advanced sync options.** After resetting, control the sync behavior with these environment variables:

| Variable | Default | Description |
| --- | --- | --- |
| `MEILI_SYNC_BATCH_SIZE` | `100` | Number of documents synced per batch. |
| `MEILI_SYNC_DELAY_MS` | `100` | Delay between sync batches, in milliseconds. |
| `MEILI_SYNC_THRESHOLD` | `1000` | Minimum number of unsynced documents before a sync is triggered. |


# Banner (https://www.librechat.ai/docs/configuration/banner)

Display important messages to all your users at the top of the app. Perfect for announcements, maintenance notices, or updates.

<Callout type="info" title="Quick Overview">
  - Only **one banner** can be active at a time
  - Schedule banners to appear and disappear automatically
  - Choose whether users can dismiss the banner or not
  - Show banners to everyone, or only logged-in users
</Callout>

---

## Creating a Banner

Run this command to create or update a banner:

```bash
npm run update-banner
```

You'll be guided through a few simple prompts:

```ansi
--------------------------
Update the banner!
--------------------------
Display From (Format: yyyy-mm-ddTHH:MM:SSZ, Default: now):
> 2025-12-02T09:00:00Z

Display To (Format: yyyy-mm-ddTHH:MM:SSZ, Default: not specified):
> 2025-12-31T23:59:59Z

Enter your message (Enter a single dot "." on a new line to finish):
> 🎉 Welcome to LibreChat! Check out our new features.
> .

Is public (y/N):
> n

Is persistable (cannot be dismissed) (y/N):
> n
```

<Callout type="tip" title="What do these options mean?">
  - **Display From/To**: When the banner should appear and disappear. Leave empty for "now" and "forever"
  - **Is public**: Show to visitors who aren't logged in (like on the login page)
  - **Is persistable**: If yes, users can't dismiss the banner — use for important notices
</Callout>

<Callout type="warning" title="Running this again replaces the existing banner">

There is only ever one banner. `update-banner` looks for an existing banner and overwrites it in place, creating one only when none exists, so you cannot queue up a second banner for a later date, and you cannot have two scheduled windows overlap. Running the command again discards the previous banner's message and schedule.

To change a banner, run `update-banner` again. To take one down before its `Display To` time, use `delete-banner` below.

</Callout>

---

## Deleting a Banner

```bash
npm run delete-banner
```

You'll see the current banner and be asked to confirm before deleting.

Removing a banner this way is also how you take one down early: there is no "disable" flag. A banner otherwise disappears on its own once `Display To` passes, and one saved with no `Display To` stays up until you delete it.

---

## Example Banners

<Tabs items={['Welcome Message', 'Maintenance Notice', 'Security Alert']}>
  <Tabs.Tab>
    A simple welcome message that users can dismiss:

    ```bash
    npm run update-banner "" "" "👋 Welcome to LibreChat!" "false" "false"
    ```
  </Tabs.Tab>
  <Tabs.Tab>
    A scheduled maintenance notice that can't be dismissed:

    ```bash
    npm run update-banner "2025-12-20T00:00:00Z" "2025-12-21T06:00:00Z" "⚠️ Scheduled maintenance on Dec 20th, 2-6 AM UTC" "true" "true"
    ```
  </Tabs.Tab>
  <Tabs.Tab>
    An urgent security notice visible to everyone:

    ```bash
    npm run update-banner "" "" "🔒 Please update your password by January 1st" "true" "true"
    ```
  </Tabs.Tab>
</Tabs>

---

## Date Format

Use this format for dates: `yyyy-mm-ddTHH:MM:SSZ`

**Examples:**
- `2025-12-25T09:00:00Z` → December 25, 2025 at 9:00 AM (UTC)
- `2025-01-01T00:00:00Z` → January 1, 2025 at midnight (UTC)

Leave the date empty to use the current time or no end date.


# Token Usage (https://www.librechat.ai/docs/configuration/token_usage)

## Intro

As of `v0.6.0`, LibreChat accurately tracks token usage for supported endpoints. All token transactions are stored in the "Transactions" collection in your database. Current releases also show real-time context usage and cost in the conversation UI when enabled.

Currently, you can limit user token usage by enabling user balances. Instead of configuring token credit limits via environment variables, you now set these options in your `librechat.yaml` file under the **balance** section. Cost values are hidden by default and must be enabled with `interface.contextCost`.

## Viewing Context Usage and Cost

LibreChat shows a context gauge while a conversation is running. The gauge updates from usage events during streaming and can show:

- Current prompt/context usage against the model's context window
- A hover summary for quick token and cost details
- A color-coded meter that separates the token categories currently using the context window
- A click-through breakdown for prompt, completion, cached token usage, branch totals, and conversation totals
- For authorized administrators, a direct Langfuse session link when the conversation has a sampled trace in the active in-app connection

The detailed breakdown is collapsed by default so the categorized meter remains compact. Users can expand it when they need the totals and LibreChat remembers that choice for later conversations. Deferred tool definitions use the same category color with a hatched treatment, while estimates without a known category composition remain a single fill.

Usage breakdowns are persisted with messages and conversations. Reopened chats retain branch and total usage/cost details instead of relying only on the active streaming session.

When summarization compacts a long conversation, LibreChat records the compacted summary baseline and counts only post-summary turns on top of it for the context gauge. Usage and cost totals still include the full branch spend.

Admins can control these displays in `librechat.yaml`:

```yaml filename="librechat.yaml"
interface:
  contextUsage: true
  contextCost: true
  currency:
    code: EUR
    rate: 0.92
```

- `contextUsage` controls whether users see the context window and token usage gauge.
- `contextCost` controls whether users see cost values in the usage details. It defaults to `false`; set it to `true` to show costs.
- `currency` converts displayed USD costs using a static multiplier when cost display is enabled. Transactions are still recorded using LibreChat's token credit accounting.

## Custom Endpoint Token Config

For custom endpoints, define model-specific context windows and per-million-token rates with `endpoints.custom[].tokenConfig`:

```yaml filename="librechat.yaml"
endpoints:
  custom:
    - name: 'Mistral'
      apiKey: '${MISTRAL_API_KEY}'
      baseURL: 'https://api.mistral.ai/v1'
      models:
        default: ['mistral-large-latest']
      tokenConfig:
        mistral-large-latest:
          prompt: 2
          completion: 6
          context: 128000
```

`prompt`, `completion`, and `context` are required for each model entry. `cacheRead` and `cacheWrite` can be added for providers that report cached input usage. For Agents that use multiple endpoints, LibreChat uses the matching endpoint/model token config when recording usage and cost.

Fetched token config is cached with user scope when endpoint models, keys, URLs, or headers can vary by request context, so isolated custom endpoint pricing and context windows stay separated.

## Transaction Configuration

The transaction system controls whether token usage records are saved to the database. This can be configured separately from the balance system.

### Transaction Settings

```yaml filename="librechat.yaml"
version: 1.2.9

# Transaction settings
# Controls whether to save transaction records to the database
# Default is true (enabled)
transactions:
  enabled: false
```

**Important:** When `balance.enabled` is set to `true`, transaction recording is automatically enabled regardless of the `transactions.enabled` setting. This ensures that balance tracking functions correctly by maintaining a complete record of all token usage.

    Check out the [Transactions Configuration](/docs/configuration/librechat_yaml/object_structure/transactions) page for more details.

## Balance Configuration

The balance system in LibreChat allows administrators to configure how token credit balances are managed for users. All balance settings are now managed in your YAML configuration under the `balance` object.

**Note:** This replaces the previous environment variables (`CHECK_BALANCE` and `START_BALANCE`) and provides a more structured way to manage user balances.

### Complete Balance Settings

```yaml filename="librechat.yaml"
version: 1.3.5

# Balance settings
balance:
  enabled: true # Enable token credit balances for users
  startBalance: 20000 # Initial tokens credited upon registration
  autoRefillEnabled: false # Enable automatic token refills
  refillIntervalValue: 30 # Numerical value for refill interval
  refillIntervalUnit: 'days' # Time unit for refill interval (days, hours, etc.)
  refillAmount: 10000 # Tokens added during each refill
```

### Balance Settings Explained

- **enabled**: Activates token credit tracking and balance management for users. When set to `true`, the system will track token usage and enforce balance limits.

- **startBalance**: Specifies the initial number of tokens credited to a user upon registration. This is the starting balance for all new users.

- **autoRefillEnabled**: Determines whether automatic refilling of token credits is enabled. When set to `true`, the system will automatically add credits to user balances based on the refill interval.

- **refillIntervalValue**: Specifies the numerical value for the interval at which token credits are automatically refilled. Works in conjunction with `refillIntervalUnit`.

- **refillIntervalUnit**: Specifies the time unit for the refill interval. Supported values include "seconds", "minutes", "hours", "days", "weeks", and "months".

- **refillAmount**: Specifies the number of tokens to be added to the user's balance during each automatic refill.

Check out the [Balance Configuration](/docs/configuration/librechat_yaml/object_structure/balance) page for more details.

## How Auto-Refill Works

When a user's balance is tracked and **autoRefill** is enabled, the system will automatically add credits to the balance only when the specified time interval has passed since the last refill. This is achieved by comparing the current date with the `lastRefill` date plus the specified interval.

### Auto-Refill Process

1. When a user attempts to spend tokens, the system checks if the current balance is sufficient
2. If the balance would drop to zero or below after the transaction, the system checks if auto-refill is enabled
3. If auto-refill is enabled, the system checks if the time interval since the last refill has passed:
   - The system compares the current date with `lastRefill + refillInterval`
   - If the interval has passed, tokens are added to the user's balance
   - The `lastRefill` date is updated to the current date
4. The transaction proceeds if the balance is sufficient (either originally or after refill)

### Supported Time Units

The `refillIntervalUnit` can be set to any of the following values:

- "seconds"
- "minutes"
- "hours"
- "days"
- "weeks"
- "months"

For example, if `refillIntervalValue` is set to 30 and `refillIntervalUnit` is `days`, the system will add `refillAmount` tokens to the user's balance only if 30 days have passed since the last refill.

### Balance Synchronization

When a user logs in, the system automatically synchronizes their balance settings with the current global balance configuration. This ensures that any changes to the balance configuration are applied to all users.

The synchronization process:

1. Checks if the user has a balance record
2. If no record exists, creates one with the current `startBalance`
3. Updates the user's auto-refill settings to match the global configuration
4. Ensures the user's refill interval and amount match the global settings

## Managing Token Balances

You can manually add or set user balances. This is especially useful during development or if you plan to build out a full balance-accruing system in the future (for example, via an admin dashboard).

### Adding Balances

```bash filename="To manually add balances, run the following command:"
# Local Development
npm run add-balance

# Docker (default setup)
docker compose exec api npm run add-balance

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run add-balance"
```

```bash filename="You can also specify the email and token credit amount to add, e.g.:"
# Local Development
npm run add-balance danny@librechat.ai 1000

# Docker (default setup)
docker compose exec api npm run add-balance danny@librechat.ai 1000

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run add-balance danny@librechat.ai 1000"
```

### Setting Balances

Additionally, you can set a balance for a user. An existing balance will be overwritten by the new balance.

```bash filename="To manually set balances, run the following command:"
# Local Development
npm run set-balance

# Docker (default setup)
docker compose exec api npm run set-balance

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run set-balance"
```

```bash filename="You can also specify the email and token credit amount to set, e.g.:"
# Local Development
npm run set-balance danny@librechat.ai 1000

# Docker (default setup)
docker compose exec api npm run set-balance danny@librechat.ai 1000

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run set-balance danny@librechat.ai 1000"
```

### Listing of balances

```bash filename="To see the balances of your users, you can run:"
# Local Development
npm run list-balances

# Docker (default setup)
docker compose exec api npm run list-balances

# Docker (deployment setup)
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run list-balances"
```

This works well to track your own usage for personal use; 1000 credits = $0.001 (1 mill USD)

## Notes on Token Usage and Balance

- With summarization enabled, you will be blocked from making an API request if the cost of the content that you need to summarize + your messages payload exceeds the current balance
- Subagent child-run model usage is recorded against the parent transaction so parent agent runs include delegated usage in their totals.
- Counting Prompt tokens is really accurate for OpenAI calls, but not 100% for plugins (due to function calling). It is really close and conservative, meaning its count may be higher by 2-5 tokens.
- The system allows deficits incurred by the completion tokens. It only checks if you have enough for the prompt Tokens, and is pretty lenient with the completion. The graph below details the logic
- The above said, plugins are checked at each generation step, since the process works with multiple API calls. Anything the LLM has generated since the initial user prompt is shared to the user in the error message as seen below.
- There is a 150 token buffer for titling since this is a 2 step process, that averages around 200 total tokens. In the case of insufficient funds, the titling is cancelled before any spend happens and no error is thrown.

![image](https://github.com/danny-avila/LibreChat/assets/110412045/78175053-9c38-44c8-9b56-4b81df61049e)

## More details

source: [LibreChat/discussions/1640](https://github.com/danny-avila/LibreChat/discussions/1640#discussioncomment-8251970)

> "rawAmount": -000, // what's this?

Raw amount of tokens as counted per the tokenizer algorithm.

> "tokenValue": -00000, // what's this?

Token credits value. 1000 credits = $0.001 (1 mill USD)

> "rate": 00, // what's this?

The rate at which tokens are charged as credits.

For example, gpt-3.5-turbo-1106 has a rate of 1 for user prompt (input) and 2 for completion (output)

| Model              | Input               | Output              |
| ------------------ | ------------------- | ------------------- |
| gpt-3.5-turbo-1106 | $0.0010 / 1K tokens | $0.0020 / 1K tokens |

Given the provided example:

```sh
    "rawAmount": -137
    "tokenValue": -205.5
    "rate": 1.5
```

```math
\text{Token Value} = (\text{Raw Amount of Tokens}) \times (\text{Rate})
```

```math
137 \times 1.5 = 205.5
```

And to get the real amount of USD spend based on **Token Value**:

```math
\frac{\text{Token Value}}{1,000,000} = \left(\frac{\text{Raw Amount of Tokens} \times \text{Rate}}{1,000,000}\right)
```

```math
\frac{205.5}{1,000,000} = \$0.0002055 \text{ USD}
```

For custom endpoints, prefer `endpoints.custom[].tokenConfig` in `librechat.yaml` for per-model rates and context windows.

## Preview

![image](https://github.com/danny-avila/LibreChat/assets/110412045/39a1aa5d-f8fc-43bf-81f2-299e57d944bb)

![image](https://github.com/danny-avila/LibreChat/assets/110412045/e1b1cc3f-8981-4c7c-a5f8-e7badbc6f675)

## Additional Notes

- With summarization enabled, API requests are blocked if the cost of the content plus the messages payload exceeds the current balance.
- The system is lenient with completion tokens, focusing primarily on prompt tokens for balance checks.
- A buffer is added for titling (approximately 150 tokens) to account for the two-step process.
- Token credits translate to monetary value (e.g., 1000 credits = $0.001 USD).

For more details and customizations, please refer to the [LibreChat Documentation](https://www.librechat.ai/docs/configuration/librechat_yaml).


# RAG API (https://www.librechat.ai/docs/configuration/rag_api)

The RAG API indexes user-uploaded files and retrieves relevant passages to augment your prompts, giving LibreChat context-aware responses grounded in your documents. It runs as a separate FastAPI service backed by a PostgreSQL + pgvector database.

<Callout type="info" title="New to RAG?">
The [RAG API Presentation](/docs/features/rag_api) explains the concept in more detail and links to a helpful video. This page covers setup and configuration.
</Callout>

## Availability

RAG works with [Agents](/docs/features/agents), as well as Custom Endpoints, OpenAI, Azure OpenAI, Anthropic, and Google.

OpenAI Assistants have their own RAG implementation through the "Retrieval" capability ([details here](https://platform.openai.com/docs/assistants/tools/knowledge-retrieval)). Using the RAG API with the Assistants API is still worthwhile since OpenAI charges for both file storage and Retrieval. This integration is planned for a future update.

## Docker Quick Start

For Docker, the RAG API is already wired up in both the default `docker-compose.yml` and `deploy-compose.yml` files, including the `RAG_API_URL` value. You only need to make sure you are running the latest image and compose files. See the [Updating LibreChat guide for Docker](/docs/local/docker#update-librechat) if you are unsure how to update.

<Callout type="warning" title="Shared .env file">
With the default Docker setup, the `.env` file is shared between LibreChat and the RAG API. Define the RAG variables in that same file. The full list lives in the [RAG API README](https://github.com/danny-avila/rag_api/blob/main/README.md).
</Callout>

Pick the embeddings provider you want to use.

<Tabs items={['OpenAI (default)', 'Hugging Face', 'Ollama']}>
<Tabs.Tab>

**Use RAG with OpenAI embeddings.** This is the default configuration.

<Steps>
<Step>

**Set the RAG API URL.** Add the following to your `.env` file:

```bash filename=".env"
RAG_API_URL=http://host.docker.internal:8000
```

</Step>
<Step>

**Provide an OpenAI API key (if needed).** If your OpenAI API key is set to `user_provided`, add a key for embeddings. Skip this step if you already supply the OpenAI key in your `.env` file.

```bash filename=".env"
RAG_OPENAI_API_KEY=sk-your-openai-api-key-example
```

</Step>
<Step>

**Start the containers.**

```bash
docker compose up -d
```

</Step>
</Steps>

</Tabs.Tab>
<Tabs.Tab>

**Use RAG with Hugging Face embeddings.**

<Steps>
<Step>

**Configure the provider.** Add the following to your `.env` file:

```bash filename=".env"
RAG_API_URL=http://host.docker.internal:8000
EMBEDDINGS_PROVIDER=huggingface
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxx
```

</Step>
<Step>

**Switch to the full RAG API image.** Update your `docker-compose.override.yml` file:

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  rag_api:
    image: registry.librechat.ai/danny-avila/librechat-rag-api-dev:latest
```

</Step>
<Step>

**Start the containers.**

```bash
docker compose up -d
```

</Step>
</Steps>

</Tabs.Tab>
<Tabs.Tab>

**Use RAG with Ollama local embeddings.**

<Callout type="info" title="Prerequisite">
You need Ollama and the `nomic-embed-text` embedding model. Pull it with `ollama pull nomic-embed-text`.
</Callout>

<Steps>
<Step>

**Configure the provider.** Add the following to your `.env` file:

```bash filename=".env"
RAG_API_URL=http://host.docker.internal:8000
EMBEDDINGS_PROVIDER=ollama
OLLAMA_BASE_URL=http://host.docker.internal:11434
EMBEDDINGS_MODEL=nomic-embed-text
```

</Step>
<Step>

**Switch to the full RAG API image.** Update your `docker-compose.override.yml` file:

```yaml filename="docker-compose.override.yml"
version: '3.4'

services:
  rag_api:
    image: registry.librechat.ai/danny-avila/librechat-rag-api-dev:latest
    # If running on Linux
    # extra_hosts:
    #   - "host.docker.internal:host-gateway"
```

</Step>
<Step>

**Start the containers.**

```bash
docker compose up -d
```

</Step>
</Steps>

</Tabs.Tab>
</Tabs>

### Lite vs. full image

Docker uses the "lite" image of the RAG API by default (`registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite:latest`), which only supports remote embeddings from OpenAI or a remote HuggingFace/Ollama service you have configured.

For local embeddings, switch the image in the compose file to the full build, `registry.librechat.ai/danny-avila/librechat-rag-api-dev:latest`. Make this change in your [Docker Compose Override File](/docs/configuration/docker_override). See `docker-compose.override.yml.example` at the root of the project for an example.

If you want a compose file that includes only the PostgreSQL + pgvector database and the Python API, see `rag.yml` at the root of the project.

### Database storage

The default compose files store the pgvector/PostgreSQL data in the Docker-managed `pgdata2` volume. This is intentional: the database files don't need to be edited directly from the host, and a managed volume avoids common ownership and permission problems. User-facing, editable files (uploads, logs, images, MongoDB data, and NGINX config) are bind-mounted to project folders where direct host access is useful.

## Local Setup

A non-container setup is more hands-on. Follow the instructions in the [RAG API repo](https://github.com/danny-avila/rag_api/).

Set `RAG_API_URL` in your LibreChat `.env` file to wherever the API is reachable from your setup. This differs from Docker, where the value is already set in the default `docker-compose.yml` file.

## Configuration

Set RAG API options through environment variables in an `.env` file accessible to the API. Most are optional, aside from the credentials and paths required by your chosen provider. In the default setup, only `RAG_OPENAI_API_KEY` is required.

### Environment Variables

<OptionTable
  options={[
    ['RAG_API_URL', 'string', 'URL of the RAG API service.', 'RAG_API_URL=http://host.docker.internal:8000'],
    ['RAG_OPENAI_API_KEY', 'string', 'OpenAI API key for embeddings. Overrides OPENAI_API_KEY for RAG.', '# RAG_OPENAI_API_KEY=sk-your-key'],
    ['RAG_OPENAI_BASEURL', 'string', 'Custom OpenAI base URL for RAG embeddings.', '# RAG_OPENAI_BASEURL='],
    ['RAG_USE_FULL_CONTEXT', 'boolean', 'Fetch entire file context instead of top 4 results. Default: false.', '# RAG_USE_FULL_CONTEXT=true'],
    ['EMBEDDINGS_PROVIDER', 'string', 'Embeddings provider: openai, azure, huggingface, huggingfacetei, or ollama. Default: openai.', '# EMBEDDINGS_PROVIDER=openai'],
    ['EMBEDDINGS_MODEL', 'string', 'Embeddings model to use. Default depends on provider.', '# EMBEDDINGS_MODEL=text-embedding-3-small'],
    ['RAG_PORT', 'number', 'Port where RAG API runs. Default: 8000.', '# RAG_PORT=8000'],
    ['RAG_HOST', 'string', 'Hostname for RAG API. Default: 0.0.0.0.', '# RAG_HOST=0.0.0.0'],
    ['COLLECTION_NAME', 'string', 'Vector store collection name. Default: testcollection.', '# COLLECTION_NAME=testcollection'],
    ['CHUNK_SIZE', 'number', 'Size of text chunks. Default: 1500.', '# CHUNK_SIZE=1500'],
    ['CHUNK_OVERLAP', 'number', 'Overlap between chunks. Default: 100.', '# CHUNK_OVERLAP=100'],
    ['OLLAMA_BASE_URL', 'string', 'Ollama base URL when using Ollama embeddings.', '# OLLAMA_BASE_URL=http://host.docker.internal:11434'],
  ]}
/>

<Callout type="info" title="Credential precedence">
`OPENAI_API_KEY` works for RAG embeddings, but `RAG_OPENAI_API_KEY` overrides it to avoid credential conflicts.
</Callout>

For the complete list of variables and their descriptions, see the [RAG API repo](https://github.com/danny-avila/rag_api/).

## Usage

Once the RAG API is running, it integrates with LibreChat automatically. When a user uploads files to a conversation, the API indexes them and uses them for context-aware responses.

<Steps>
<Step>

**Upload files to the conversation.** If `RAG_API_URL` is not configured or not reachable, the upload fails.

</Step>
<Step>

**Chat as usual.** As the user interacts with the model, the RAG API retrieves relevant passages from the indexed files based on the input and uses them to augment the prompt.

</Step>
<Step>

**Control when files are queried.** By default, the vector store is queried on every new message in a conversation that has a file attached. Craft your prompts accordingly.

Toggle **Resend Files** off in the conversation settings to query files only when they are explicitly attached to a message.

<Frame>
![Resend Files toggle in conversation settings](https://github.com/danny-avila/LibreChat/assets/110412045/29a2468d-85ac-40d7-90be-a945301c5729)
</Frame>

</Step>
<Step>

**Reuse indexed files.** Upload a file once, then attach it to any new message or conversation from the Side Panel.

<Frame>
![Attaching indexed files from the Side Panel](https://github.com/danny-avila/LibreChat/assets/110412045/b40cb3d3-e6e7-46ec-bc74-65d194f55a1e)
</Frame>

Files must be in "Host" storage. "OpenAI" files are treated differently and are exclusive to Assistants, so they must not have been uploaded while the Assistants endpoint was selected and active. View and manage your files from the Side Panel.

<Frame>
![Viewing and managing files from the Side Panel](https://github.com/danny-avila/LibreChat/assets/110412045/1f27e974-4124-4ee3-8091-13514cb4cbca)
</Frame>

</Step>
</Steps>

## Troubleshooting

If you run into issues setting up or using the RAG API:

- Confirm all required environment variables are set correctly in your `.env` file.
- Make sure the vector database is configured and accessible.
- Verify that the OpenAI API key or other provider credentials are valid.
- Check both the LibreChat and RAG API logs for errors or warnings.

If the problem persists, refer to the RAG API documentation or ask the LibreChat community on GitHub Discussions or Discord.


# Overview (https://www.librechat.ai/docs/features)

<FeaturesHub />


# MCP (https://www.librechat.ai/docs/features/mcp)

Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to Large Language Models (LLMs). Think of MCP as the **"USB-C of AI"** - just as USB-C provides a universal connection standard for electronic devices, MCP offers a standardized way to connect AI models to diverse tools, data sources, and services.

LibreChat leverages MCP to dramatically expand what your AI agents can do, allowing you to integrate everything from file system access, web browsers, specialized APIs, to custom business tools.

## Why MCP Matters

LLMs are limited to their built-in capabilities. With MCP, LibreChat breaks down these walls by:

- **Connecting to any tool or service** that provides an MCP server
- **Standardizing integrations** so you don't need to edit LibreChat's code for each tool
- **Supporting multi-user environments** with proper authentication and isolation
- **Providing a growing ecosystem** of dynamic, ready-to-use integrations

## How MCP Works in LibreChat

LibreChat provides two ways to use MCP servers, either in the chat area or with agents.

You can configure MCP servers manually in your `librechat.yaml` file or by using [smithery.ai](https://smithery.ai) to find and install MCP servers into `librechat.yaml` ([see example below](#basic-configuration)). Any time you add or edit an MCP server, you will need to restart LibreChat to initialize the connections.

### OAuth Callback URL

For OAuth-enabled MCP servers, the LibreChat callback URL is:

```text
${DOMAIN_SERVER}/api/mcp/<server-name>/oauth/callback
```

`<server-name>` is the key used under `mcpServers` in `librechat.yaml` or the server name created in the MCP Settings UI. For example, a server named `salesforce` with `DOMAIN_SERVER=https://chat.example.com` uses `https://chat.example.com/api/mcp/salesforce/oauth/callback`.

Register this exact callback URL with the OAuth provider. Local Docker installs usually use `http://localhost:3080` as the base URL.

### In Chat Area

![MCP Tools in Chat Area](/images/agents/mcp_chat.png)

LibreChat displays configured MCP servers directly in the chat area when using traditional endpoints (OpenAI, Anthropic, Google, Bedrock, etc.):

- Select any non-agent endpoint first, and a tool-compatible model
- MCP servers appear in a dropdown in the chat interface below your text input
- When selected, all tools from that server become available to your current model
- Quick access to MCP tools without creating an agent, allowing multiple servers to be used at once

To keep an MCP server out of regular chat selection, set [`chatMenu: false`](/docs/configuration/librechat_yaml/object_structure/mcp_servers#chatmenu) in your configuration:

```yaml
mcpServers:
  internal-tools:
    command: npx
    args: ['-y', 'internal-mcp-server']
    chatMenu: false # Not selectable in regular chat
```

This setting is enforced on chat requests as well as in the picker, so a stale saved selection cannot continue using a hidden server. It does not remove the server from saved Agents or from a model spec that explicitly assigns it. LibreChat also omits servers that a user can reach only through an authorized Agent from the regular chat picker and request path.

### With Agents

MCP servers integrate seamlessly with LibreChat Agents:

1. Create or edit an agent
2. Open **Add tools** in the Agent Builder and select **MCP**
3. Select an MCP server; each server appears as a single catalog entry
4. Expand the selected server to enable or disable individual tools
5. Save your agent

![MCP Tools in Agent Builder](/images/mcp/mcp_ui_agent.png)

This higher-level organization keeps the interface manageable - even servers with 20+ tools (like Spotify) appear as single entries that can be expanded for granular control.

### Basic Configuration

Add MCP servers to your `librechat.yaml` file manually:

```yaml
mcpServers:
  # ClickHouse Cloud
  clickhouse-cloud:
    type: streamable-http
    url: https://mcp.clickhouse.cloud/mcp

  # File system access
  filesystem:
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-filesystem'
      - /path/to/your/documents

  # Web browser automation
  puppeteer:
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-puppeteer'

  # Production-ready cloud service
  business-api:
    type: streamable-http
    url: https://api.yourbusiness.com/mcp
    headers:
      X-User-ID: '{{LIBRECHAT_USER_ID}}'
      Authorization: 'Bearer ${API_TOKEN}'
    timeout: 30000
    serverInstructions: true
```

### Adding MCP Servers in the UI

You can also add and configure MCP servers directly from the LibreChat interface without editing any configuration files or restarting the server.

#### Step 1: Open the MCP Settings Panel

Navigate to the **MCP Settings** panel from the right sidebar. You'll see any existing MCP servers listed here along with a **+** button to add new ones.

![MCP Settings Panel](/images/mcp/mcp_ui_settings_panel.png)

#### Step 2: Fill Out Server Details

Press the **+** button and fill out your MCP server name, description, URL, transport type, and authentication method, then click **Create**.

![Add MCP Server Dialog](/images/mcp/mcp_ui_configure.png)

Your new server will appear in the MCP Settings panel with a confirmation toast.

![MCP Server Created Successfully](/images/mcp/mcp_ui_success_registered.png)

#### Step 3: Check Connection Status and Authenticate

Review the [connection status indicator](#connection-status-indicators) for your new server. If the server requires OAuth authentication, the status will show as disconnected. Click the server's authenticate/connect button (you can do this either by clicking on the MCP server itself in the chat dropdown menu, or by clicking on the connection icon first to be taken to a dialog with more information on the connection state) to begin the authentication flow.

![Connection Status - Disconnected](/images/mcp/mcp_ui_unconnected.png)

Once initiated, the status indicator will update to show that authentication is in progress.

![Connection Status - Authenticating](/images/mcp/mcp_ui_authenticating.png)

#### Step 4: Continue in the OAuth Tab

A new browser tab will open for the OAuth provider. Verify the callback URL and click **Continue** to authorize LibreChat.

![OAuth Continue Prompt](/images/mcp/mcp_ui_callback_confirm.png)

#### Step 5: Authentication Successful

After authenticating, you'll see a success confirmation. This window will automatically close and redirect you back to LibreChat.

![Authentication Successful](/images/mcp/mcp_ui_success_redirect.png)

#### Step 6: Server Ready for Use

LibreChat acknowledges the successful authentication and automatically selects the MCP server for use within your conversation. The server now shows a connected status indicator and is checked in the MCP Servers dropdown.

![MCP Server Authenticated and Auto-Selected](/images/mcp/mcp_ui_done.png)

Your new MCP server is also available in the Agent Builder, where you can add its tools to any agent and customize what subset of tools are allowed.

![MCP Server Available in Agent Builder](/images/mcp/mcp_ui_agent.png)

#### Credential Variables for UI-Created Servers

When adding an MCP server through the UI, you can require users to provide their own API keys. In the Authentication section of the MCP Server Builder dialog, select "API Key" and check **"User provides key"**. Choose the header format (Bearer, Basic, or Custom) and save the server.

Behind the scenes, LibreChat automatically creates a `customUserVars` entry named `MCP_API_KEY` and configures the appropriate header template (e.g., `Authorization: Bearer {{MCP_API_KEY}}`). Each user provides their own key through the MCP Tool Select Dialog when configuring an agent — the same UI used for [YAML-defined `customUserVars`](#user-provided-credentials).

When an administrator or user API key is configured, LibreChat treats the server as API-key authenticated and skips OAuth auto-detection. An explicit OAuth configuration still takes precedence. If a UI-created API-key server was saved by an older LibreChat version and remains stuck in **OAuth Required**, edit and save the server once so its stored connection metadata is refreshed.

<Callout type="warning">
For security, UI-created (DB-sourced) MCP servers can **only** resolve `customUserVars` placeholders (`{{VAR_NAME}}`). Server-side environment variables (`${ENV_VAR}`), user profile fields (`{{LIBRECHAT_USER_*}}`), and OIDC tokens (`{{LIBRECHAT_OPENID_*}}`) are intentionally blocked to prevent unauthorized access to server secrets or other users' data. For full placeholder support, configure the server in `librechat.yaml` instead.
</Callout>

### Adding MCP Servers with Smithery

Smithery.ai provides a streamlined way to discover and install MCP servers for LibreChat. Follow these steps to get started:

#### Step 1: Search for MCP Servers

Visit [smithery.ai](https://smithery.ai) and search for the MCP server you want to add to your LibreChat instance.

![Smithery Search Interface](/images/mcp/mcp_smithery_search.png)

#### Step 2: Select Your MCP Server

Click on the MCP server from the search results to view details and available tools.

![MCP Server Details Page](/images/mcp/mcp_smithery_mcp_server.png)

#### Step 3: Configure for LibreChat

Navigate to the **Auto** tab in the **Connect** section and select **LibreChat** as your desired client.

![LibreChat Integration Setup](/images/mcp/mcp_smithery_librechat.png)

#### Step 4: Install the MCP Server

Copy and run the generated command in your terminal to install the MCP server.

![Installation Command](/images/mcp/mcp_smithery_copy.png)

#### Step 5: Restart and Verify

Your MCP server is now installed and configurable in `librechat.yaml`. Restart LibreChat to initialize the connections and start using your new MCP server.

![MCP Server Successfully Installed](/images/mcp/mcp_show_installed.png)
_MCP server installed through smithery.ai and ready for use in LibreChat_

For detailed configuration options and examples, see:

- [MCP Server Guides](/docs/mcp_servers)
- [MCP Servers Configuration](/docs/configuration/librechat_yaml/object_structure/mcp_servers)
- [Agent Configuration](/docs/configuration/librechat_yaml/object_structure/agents)
- [Advanced Agent Features](/docs/features/agents#model-context-protocol-mcp)
- [Agent Plugins](/docs/features/agent_plugins) for experimentally bundling deployment MCP servers with Skills

## MCP Server Management

LibreChat provides comprehensive tools for managing MCP server connections with connection status tracking and OAuth authentication and initialization support in the UI.

### Connection Status Indicators

LibreChat displays dynamic status icons showing the current state of each MCP server in the chat dropdown and settings panel:

![MCP Server Status Icons](/images/mcp/mcp_server_status_icons.png)

**Status Types:**

- **Connected** (green gear): Server is connected and has configurable customUserVars
- **OAuth Required** (amber key): Server requires OAuth authentication
- **Disconnected** (orange plug): Server connection failed or lost
- **Initializing** (blue loader): Server is starting up or reconnecting
- **Error** (red triangle): Server encountered an error
- **Cancelling** (red x): OAuth flow is being cancelled

### Server Initialization

You can initialize or re-initialize MCP servers directly from the interface:

**One click:**

- One-click initialization from the MCP server selection dropdown

  <Video src="/videos/mcp_one_click_init.mp4" title="One-click MCP initialization" />

**From MCPConfigDialog:**

- Click the status icon next to an MCP server in the Chat Dropdown to open the MCPConfigDialog
- Configure custom user variables and click the Authenticate/Initialize button depending on the server authentication type

       <Video src="/videos/mcp_config_dialog_auth.mp4" title="MCP config dialog authentication" />

  **From MCP Settings Panel:**

- Click any server in the server list section of MCP Settings Panel to access configuration and initialization controls
- Configure custom user variables and click the Authenticate/Initialize button depending on the server authentication type

  <Video src="/videos/mcp_settings_init.mp4" title="MCP settings panel initialization" />

Initialization failures now distinguish an unreachable server, missing `customUserVars`, an OAuth connection that must be authenticated again, and a generic initialization failure. Use the named missing variables or reauthentication action in the message before retrying; the server no longer needs to be removed and re-added for these cases.

If an Agent explicitly expects MCP tools but none can be resolved at run start, LibreChat fails the run with connection and access guidance instead of silently running without those tools.

MCP servers that emit `notifications/tools/list_changed` can update their tool catalog without a LibreChat restart. LibreChat fetches the current list from the live connection, publishes added tools, and stops advertising tools the server removed. Reconnects also synchronize the catalog before the connection is treated as ready. Managed snapshot refreshes and cold-server discovery share one process-wide three-operation gate. Once admitted, each operation remains bounded by its tool-list or per-server discovery deadline, including any shorter `initTimeout`, so slow catalog work cannot hold a slot indefinitely.

For Streamable HTTP servers, a reconnect can briefly return HTTP `409` when the server still holds a stale standalone SSE stream for the old session. LibreChat treats this as transient, tears down the stale session, and rebuilds the connection automatically; no operator action is needed when recovery succeeds. If the rebuilt connection also fails, LibreChat continues to surface the underlying connection error.

LibreChat classifies Streamable HTTP responses from the parsed `Content-Type` media type rather than a substring match. Parameters cannot make a non-SSE response appear to be `text/event-stream`, so response-size errors continue through the correct SSE or non-SSE handling path.

### MCP Settings Panel Visibility

The MCP Settings Panel appears in the right sidebar when LibreChat detects MCP servers that might require user intervention during their initialization. The panel will be visible when any configured server meets one of these criteria:

- **Custom User Variables**: Server has `customUserVars` defined which may contain user-provided credentials
- **OAuth Authentication**: Server is detected as requiring OAuth authentication during startup
- **Manual Initialization**: Server has `startup: false` configured, requiring manual initialization

## LibreChat-Specific Features

LibreChat's MCP implementation is designed for highly configurable, real-world, multi-user environments.

### User-Specific Connections

- Each user gets their own isolated connection to MCP servers
- User authentication and permissions are respected
- Personal data and context remain private

### Sharing MCP Servers

MCP servers participate in LibreChat's [granular access control](/docs/features/access_control) system. In addition to servers defined in `librechat.yaml` (which are managed by admins and governed by [`interface.mcpServers`](/docs/configuration/librechat_yaml/object_structure/interface#mcpservers) feature permissions), user-created MCP servers have their own ACL and can be shared with specific **users**, **groups**, **roles**, or **publicly**, at Viewer, Editor, or Owner level.

The `USE`, `CREATE`, `SHARE`, and `SHARE_PUBLIC` feature flags under `interface.mcpServers` control who is allowed to create and share MCP servers at all. See [Access Control](/docs/features/access_control) for how the permission layers compose.

### Dynamic User Context

MCP servers can access user information through placeholders in **URLs and headers** (for SSE and Streamable HTTP transports):

```yaml
mcpServers:
  user-api:
    type: streamable-http
    url: https://api.example.com/users/{{LIBRECHAT_USER_USERNAME}}/mcp
    headers:
      X-User-ID: '{{LIBRECHAT_USER_ID}}'
      X-User-Email: '{{LIBRECHAT_USER_EMAIL}}'
      X-User-Role: '{{LIBRECHAT_USER_ROLE}}'
      Authorization: 'Bearer ${API_TOKEN}'
```

Available placeholders include:

- `{{LIBRECHAT_USER_ID}}` - Unique user identifier
- `{{LIBRECHAT_USER_EMAIL}}` - User's email address
- `{{LIBRECHAT_USER_ROLE}}` - User role (admin, user, etc.)
- `{{LIBRECHAT_USER_USERNAME}}` - Username
- And many more (see [MCP Servers Configuration](/docs/configuration/librechat_yaml/object_structure/mcp_servers#headers) for complete list)

YAML-defined MCP servers can also use `{{LIBRECHAT_OPENID_*}}`, `{{LIBRECHAT_GRAPH_*}}`, and `{{LIBRECHAT_BODY_*}}` placeholders. `{{LIBRECHAT_BODY_*}}` values are request-scoped, so LibreChat creates connections for the active run, reuses them across tool calls in that run, and cleans them up when the request ends. Request-scoped servers are excluded from the persistent tool cache so request-specific headers and URLs are not reused outside the active run. Because their tool list cannot be resolved before a chat request exists, the Agent Builder exposes one runtime-tools selection for the complete server and resolves its tools at run time. Completing OAuth stores authorization but defers the connection until the first run supplies request context. User, OpenID, and Graph placeholders are user-scoped; HTTP transports refresh their resolved headers before each tool call without requiring a reconnect by themselves.

`{{LIBRECHAT_GRAPH_ACCESS_TOKEN}}` exchanges the user's reusable OpenID access token through the Microsoft on-behalf-of flow. [`GRAPH_API_SCOPES`](/docs/configuration/dotenv#microsoft-graph-api--entra-id-integration) sets the requested space-separated scopes and defaults to `https://graph.microsoft.com/.default`. This is independent of `OPENID_GRAPH_SCOPES`, which configures people and group search.

The `{{LIBRECHAT_BODY_PARENTMESSAGEID}}` placeholder is populated for native Agent chats, OpenAI-compatible Agents API calls, and legacy Assistants runs. Open Responses has no equivalent parent-message identity and fails closed when an MCP server requires that value. See the [MCP server placeholder reference](/docs/configuration/librechat_yaml/object_structure/mcp_servers#headers).

### Server Instructions

`serverInstructions` is a LibreChat feature that dynamically adds configured instructions when any tool from that MCP server is selected:

```yaml
mcpServers:
  filesystem:
    command: npx
    args: ['-y', '@modelcontextprotocol/server-filesystem', '/docs']
    serverInstructions: |
      When accessing files:
      - Always check file permissions first
      - Use absolute paths for reliability
      - Handle errors gracefully
```

Options:

- `true`: Use instructions advertised by the server when available
- `false`: Disable instructions
- `string`: Use the custom instructions verbatim (shown above)

LibreChat preserves this configured declaration separately from instructions fetched while inspecting the server. A `true` declaration therefore remains enabled across inspection and cached registry reloads, while a custom string remains authoritative.

### Timeout Configuration

For long-running MCP operations, configure appropriate timeouts for both initialization and tool operations.

```yaml
mcpServers:
  data-processor:
    type: streamable-http
    url: https://api.example.com/mcp
    initTimeout: 15000 # 15 seconds for server initialization
    timeout: 60000 # 60 seconds for tool operations
```

**Note**: If operations are still being cut short, check your proxy configuration (e.g., nginx, traefik, etc.) which may be severing connections prematurely due to default timeouts.

When a user presses **Stop** during a foreground Agent tool call, LibreChat forwards the run cancellation signal to MCP. For Streamable HTTP servers, the MCP SDK sends `notifications/cancelled`; the server must honor that notification to stop its own work. Detached background calls intentionally keep a separate lifecycle and are not cancelled with the foreground turn.

### User Provided Credentials

You can allow users to provide their own credentials for MCP servers through `customUserVars`. This enables secure, user-specific authentication without storing credentials in configuration files.

```yaml
mcpServers:
  my-api-server:
    type: streamable-http
    url: 'https://api.example.com/mcp'
    headers:
      X-Auth-Token: '{{MY_API_KEY}}' # Uses the user-provided value
    customUserVars:
      MY_API_KEY:
        title: 'API Key'
        description: "Enter your personal API key from <a href='https://example.com/keys' target='_blank'>your account settings</a>"
```

Users can configure these credentials:

- **From Chat Area**: Click the settings icon next to configurable MCP servers in the tool selection dropdown
- **From MCP Settings Panel**: Access "MCP Settings" in the right panel to manage credentials for all configured servers

#### Reinitializing MCP Servers with User Credentials

For MCP servers that require user-specific credentials before they can be used (e.g., `PAT_TOKEN`'s in [GitHub’s official MCP server](https://github.com/github/github-mcp-server)), LibreChat allows users to provide these credentials and then reinitialize the MCP server from within the UI without restarting the whole application:

1. When you select an MCP that uses `customUserVars`, you will be able to **Save** or **Revoke** a `customUserVar`'s value for the selected MCP server from within the MCP Panel.
2. After saving a value for a `customUserVar`, click the reinitialize button (an icon with circular arrows next to each server name in the MCP Panel).
3. LibreChat will attempt to connect to the server using your provided credentials and notify you with a toast whether the reinitialization process has succeeded or failed.

> _Tip: If you know a server will require credentials not available at first startup, you can add `startup: false` to its configuration. This tells LibreChat to not attempt to connect to that server until it is manually reinitialized in the UI._

**Example:**

```yaml
mcpServers:
  github-mcp:
    type: streamable-http
    url: 'https://api.githubcopilot.com/mcp/'
    headers:
      Authorization: '{{PAT_TOKEN}}'
    customUserVars:
      PAT_TOKEN:
        title: 'GitHub PAT Token'
        description: 'GitHub Personal Access Token'
    startup: false
```

### OAuth Authentication

LibreChat supports OAuth authentication for MCP servers, following Anthropic's recommendation for secure MCP connections. OAuth provides a standardized, secure way to authenticate without storing long-lived credentials.

#### Supported OAuth Flows

LibreChat MCP servers support OAuth 2.0 with:

- **Authorization Code Flow with PKCE**: Recommended for maximum security
- **Client Discovery**: Automatic client registration when supported by the OAuth provider
- **Refresh Tokens**: Automatic token renewal when available
- **Token endpoint discovery**: Preconfigured clients can discover whether the provider expects client credentials in the POST body or an HTTP Basic authorization header

In the MCP Builder, **Token Exchange Method** offers **Auto**, **Default (POST request)**, and **Basic authorization header**. Auto leaves the method unset so LibreChat can use trusted provider metadata, falling back to Basic authentication when the provider does not advertise a method.

#### Configuration Examples

```yaml
mcpServers:
  # Public remote MCP server for PayPal, uses OAuth Client Discovery
  # ❌ Refresh Tokens: you may need to re-authenticate periodically
  # More info: https://developer.paypal.com/tools/mcp-server/
  paypal:
    type: 'sse'
    initTimeout: 150000 # higher timeout to allow for initial authentication
    url: 'https://mcp.paypal.com/sse'

  # Example self-hosted remote MCP server for Spotify, uses OAuth Client Discovery
  # ✅ Refresh Tokens: refreshes token for authentication automatically
  # Hosted on Cloudflare Workers, more info: https://github.com/LibreChat-AI/spotify-mcp
  spotify:
    type: 'streamable-http'
    initTimeout: 150000
    url: 'https://mcp-spotify-oauth-example.account.workers.dev/mcp'
```

#### OAuth Authentication Flow

When you first configure an OAuth-enabled MCP server:

1. **Initial Connection**: LibreChat attempts to connect to the MCP server
2. **Authentication Required**: If no valid token exists, you'll see an OAuth authentication indicator in the chat dropdown for that server
3. **Button Interface**: Click the authentication indicator to open the MCP configuration dialog and begin the OAuth flow
4. **Continue or use another device**: Continue in the current browser, copy the authorization link, or reveal a QR code to open it on another device
5. **Browser Redirect**: Complete authentication with the OAuth provider
6. **Return Handling**: LibreChat automatically processes the OAuth callback once you've authenticated
7. **Token Storage**: LibreChat securely stores the tokens for future use
8. **Connection Established**: Once you've authenticated, the MCP server will be connected and you can use it in your chat

#### OAuth Callback URL

When an MCP server uses OAuth, LibreChat exposes a callback endpoint that the OAuth provider redirects to after successful authorization.

The callback URL must follow this format:

`${baseUrl}/api/mcp/${serverName}/oauth/callback`

Where `${serverName}` is the MCP server key defined in your `librechat.yaml` configuration. LibreChat handles the redirect at this endpoint, completes the token exchange, and associates the credentials with the corresponding MCP server.

<Callout type="info" title="OAuth Callback URL Example">
Given the following MCP server configuration:

```yaml
mcpServers:
  # Example self-hosted remote MCP server for Spotify, uses OAuth Client Discovery
  # ✅ Refresh Tokens: refreshes token for authentication automatically
  # Hosted on Cloudflare Workers, more info: https://github.com/LibreChat-AI/spotify-mcp
  spotify:
    type: 'streamable-http'
    initTimeout: 150000
    url: 'https://mcp-spotify-oauth-example.account.workers.dev/mcp'
```

The callback URL would be `${baseUrl}/api/mcp/spotify/oauth/callback`.

</Callout>

Note:

- The callback URL must be registered exactly with the OAuth provider for the flow to work.
- Other paths such as `/api/oauth/callback` or `/api/oauth/openid/callback` are not valid for MCP OAuth flows.

#### Token Management

LibreChat handles OAuth tokens intelligently:

- **Secure Storage**: Tokens are encrypted and stored securely
- **Automatic Refresh**: When refresh tokens are available, LibreChat automatically renews expired access tokens
- **Silent 401 Recovery**: If an OAuth MCP connection receives a mid-session authentication rejection, LibreChat coordinates one bounded refresh and reconnect attempt per user and server. Concurrent callers share that recovery result before LibreChat surfaces a new authentication prompt.
- **Session Management**: Each user maintains their own OAuth sessions for multi-user environments

If a saved Agent stream is reloaded or resumed while an MCP authorization prompt is still pending, LibreChat reconstructs that prompt from the durable generation state. Completed, cancelled, failed, or expired prompts are not restored.

Stored OAuth clients and tokens are bound to the MCP server URL and OAuth client/endpoint settings. Changing those settings requires users to authenticate again. When editing a UI-created server that retains a stored `client_secret`, re-enter the secret when changing a binding field such as the server URL, client ID, authorization URL, token URL, or token authentication method.

In horizontally scaled deployments, OAuth flow status and bound-token readiness can be reconciled across LibreChat pods. The UI reports a server as ready only after post-OAuth initialization succeeds, even when the callback and status poll reach different pods. Callbacks from superseded attempts are rejected.

When Redis is enabled, application-level MCP catalog generations and publication revisions are coordinated across replicas. Redis Cluster deployments use slot-safe catalog keys and updates, so startup synchronization and `tools/list_changed` refreshes do not require a single-node Redis layout.

Each user will be prompted to authenticate with their own OAuth login when they first use an OAuth-enabled MCP server. This ensures that connection and authentication details are unique to each user, maintaining security and privacy in multi-user environments.

#### OAuth Timing

MCP OAuth completion uses its own server-configured timeout instead of reusing the MCP server `initTimeout`. By default, LibreChat waits up to 10 minutes for a user to complete MCP OAuth and keeps the flow state for 15 minutes.

Use these environment variables when an OAuth provider or user workflow needs more time:

```bash filename=".env"
MCP_OAUTH_HANDLING_TIMEOUT=600000
MCP_OAUTH_FLOW_TTL=900000
```

`MCP_OAUTH_FLOW_TTL` is clamped to outlive `MCP_OAUTH_HANDLING_TIMEOUT`, so callbacks near the deadline can still find their flow state. The MCP server-card polling window follows the configured handling timeout.

![User-specific OAuth authentication flow](/images/agents/mcp_oauth_flow.png)

> Note: The tokens shown during app startup are for app-level initialization only and are not used for individual user connections.

Example of automatic token refresh:

```bash
[MCP][spotify] Access token missing
[MCP][spotify] Attempting to refresh token
[MCP][spotify] Successfully refreshed and stored OAuth tokens
[MCP][spotify] ✓ Initialized
```

#### Best Practices

1. **Use OAuth when available**: Prefer OAuth over API keys for better security
2. **Configure appropriate timeouts**: Use `MCP_OAUTH_HANDLING_TIMEOUT` and `MCP_OAUTH_FLOW_TTL` for OAuth completion windows; use `initTimeout` for server initialization
3. **Monitor token expiration**: Check logs for authentication issues
4. **Plan for re-authentication**: Some providers don't support refresh tokens

## Server Transports

MCP servers can be configured to use different transport mechanisms:

**STDIO Servers**

- Work well for local, single-user environments
- Not scalable for remote or cloud deployments
- The bundled MCP SDK limits each individual STDIO message to 10 MB. A larger single result closes the transport with an error; this limit is separate from the total size of a session or stream.

**Server-Sent Events (SSE) Servers**

- Remote transport mechanism but not recommended for production environment

**Streamable HTTP Servers**

- Uses HTTP POST for sending messages and supports streaming responses
- Operates as an independent process that can handle multiple client connections
- Supports both basic requests and streaming via Server-Sent Events (SSE)
- More performant alternative to the legacy HTTP+SSE transport
- Supports proper multi-user server configurations

**For production environments**, only MCP servers with ["Streamable HTTP" transports](https://modelcontextprotocol.io/specification/draft/basic/transports/streamable-http) are recommended. Unlike SSE which maintains long-running connections, Streamable HTTP offers stateless options that are better suited for scalable, multi-user deployments.

LibreChat is at the forefront of implementing flexible, scalable MCP server integrations to support diverse usage scenarios and help you build the AI workflows of tomorrow.

---

**Ready to extend your AI capabilities?** Start by configuring your first MCP server and discover how LibreChat can connect to virtually any tool or service your organization needs.


# Agents (https://www.librechat.ai/docs/features/agents)

# Agents: Build Custom AI Assistants

LibreChat's AI Agents feature provides a flexible framework for creating custom AI assistants powered by various model providers.

This feature is similar to OpenAI's Assistants API and ChatGPT's GPTs, but with broader model support and a no-code implementation, letting you build sophisticated assistants with specialized capabilities.

## Getting Started

To create a new agent, select "Agents" from the endpoint menu and open the Agent Builder panel found in the Side Panel.

![Agents - Endpoints Menu](/images/agents/endpoints_menu.png)

The creation form includes:

- **Avatar**: Upload a custom avatar to personalize your agent
- **Name**: Choose a distinctive name for your agent
- **Description**: Optional details about your agent's purpose
- **Instructions**: System instructions that define your agent's behavior
- **Model**: Select from available providers and models

The **Tools** and **Skills** controls open searchable libraries for built-in capabilities, tools, MCP servers, Actions, and Skills. Select an item to configure it, then save the agent.

**Existing agents can be selected from the top dropdown of the Side Panel.**

- **Also by mention with "@" in the chat input.**

![Agents - Mention](/images/agents/mention.png)

### Tool Library

The Agent Builder keeps enabled items in separate **Tools** and **Skills** sections. Open **Add tools** to search the complete catalog or filter it by **Official**, **Tools**, **MCP**, or **Actions**. **Made by you** collects MCP servers and Actions you created, while **Favorites** keeps starred items available across agents. Each user can save up to 100 tool-library favorites.

Select an item to configure it without leaving the builder. The **Create new** menu appears only when the user has permission to create MCP servers or Actions. Removing a file-backed capability such as File Search, File Context, or Code Interpreter opens its file manager when attached files still need to be reviewed. A file-backed capability remains selected and is preserved on save while it still owns files; remove or reassign those files before removing the capability itself.

Skills use their own picker and can be selected individually or enabled as the complete accessible catalog. See [Skills](/docs/features/skills#agent-scope).

Operators can also use experimental [Agent Plugins](/docs/features/agent_plugins) to bundle deployment Skills and MCP servers into startup-loaded packages.

### Model Configuration

The model parameters interface allows fine-tuning of your agent's responses:

- Temperature (0-1 scale for response creativity)
- Max context tokens
- Max output tokens
- Image detail (`low`, `auto`, or `high`) for native vision image inputs
- Additional provider-specific settings

Agent instructions can use `{{current_date}}`, `{{current_datetime}}`, and `{{iso_datetime}}`. LibreChat resolves them from the server-captured start of the logical turn and keeps that timestamp stable across provider loops, Subagent initialization, and human-in-the-loop resume. The first two use the request time zone; `{{iso_datetime}}` remains UTC.

Recognized model-not-found and provider rate-limit failures render localized guidance. Other provider errors retain their useful provider text but omit LangChain troubleshooting URLs before the message is persisted. When [model-bound content protection](/docs/configuration/librechat_yaml/object_structure/message_filter#source-aware-filters) is enabled, its generic protected error still takes precedence.

### Version History

Users with edit access can open **Version History** from the Agent Builder to inspect saved configurations in a timeline. Each entry shows when it was saved and summarizes its tools and capabilities. Restoring an earlier entry requires confirmation and replaces the current agent configuration with that saved state. Saving always applies the Agent's current changes, even when the resulting configuration matches the newest history entry and LibreChat does not add a duplicate entry.

## Agent Capabilities

> **Note:** All capabilities can be toggled via the `librechat.yaml` configuration file. See [docs/configuration/librechat_yaml/object_structure/agents#capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) for more information.

### Code Interpreter

When enabled, the Code Interpreter capability allows your agent to:

- Execute code in multiple languages, including:
  - Python, JavaScript, TypeScript, Go, C, C++, Java, PHP, Rust, and Fortran
- Process files securely through the LibreChat Code Interpreter API
- Run code without local setup, configuration, or sandbox deployment
- Handle file uploads and downloads seamlessly
- [More info about the Code Interpreter API](/docs/features/code_interpreter)
  - **Powered by the open-source [code-interpreter](https://github.com/ClickHouse/code-interpreter) service (self-hosted)**

When every required Code Interpreter file from a conversation fails to restore into the current sandbox, LibreChat stops the Agent before invoking the model and asks the user to reattach the files. This prevents the run from continuing with stale or inaccessible file references.

When conversation files share a Code Interpreter filename, LibreChat assigns distinct sandbox destinations and tells the Agent the resolved paths. See [Seamless File Handling](/docs/features/code_interpreter#seamless-file-handling) for collision behavior and the remaining multi-Agent private-file limitation.

#### Stateful Code Sessions

Stateful code sessions let an agent reuse a sandbox workspace across code executions. Files, installed packages, and working state usually carry over, which is useful for iterative analysis and multi-step file work. In the Agent Builder, the **Stateful environment** selector controls who shares that workspace:

- **User workspace (recommended):** the signed-in user's stateful agents share one workspace
- **Agent + user workspace:** each user gets a separate workspace for each agent
- **Conversation workspace:** each user gets a separate workspace for each conversation

Administrators can also configure named backends under [`statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions). When present, the separate **Execution environment** selector chooses the managed Code API, operator-attached VM, or personal worker that owns the workspace; **Deployment default** uses the configured default executable environment. The sharing scope above remains independent of that backend choice.

When a deployment enables self-service workers, users with Code Environment management permission can open **Settings > Code environments** to pair an outbound `@librechat/code` worker on their own VM, select it in the Agent Builder, and revoke it later. Personal environments are owner-bound and use the same route for code, shell, and Programmatic Tool Calling. See [Attached environments and pairing](/docs/features/code_interpreter#attached-environments-and-pairing) for the experimental setup and security model.

New Agents initially use the signed-in user's **Default stateful workspace** from **Settings > Data Controls > Code execution**. The personal default is **User workspace** until changed. Updating this preference does not alter existing Agents or enable Stateful Code Sessions by itself. Administrators can restrict the available scopes with [`statefulCodeSessions.allowedEnvironments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions); when a saved personal default is no longer allowed, new Agents use the first permitted scope.

<Callout type="warning" title="Highly experimental">
  Stateful Code Sessions are in an early experimentation phase. Their behavior, configuration, persistence characteristics, and underlying integration may change substantially. Do not treat the current implementation as a stable production contract.
</Callout>

This feature is opt-in. An administrator must add `stateful_code_sessions` to the [agent capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) and configure a dedicated stateful Code Interpreter route through [`LIBRECHAT_CODE_BASEURL_STATEFUL`](/docs/configuration/dotenv#stateful-code-interpreter-endpoint) or named [`statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions). Code Interpreter must be enabled on the agent, and **Stateful code sessions** must be turned on under its Advanced settings.

Stateful requests do not fall back to the normal stateless Code Interpreter endpoint when the dedicated URL is missing or incompatible. Stateful and stateless sessions also do not share live files or installed packages. The stateful workspace may reset at any time, regardless of its scope. Save important outputs under `/mnt/data`, and do not otherwise rely on session state as durable storage.

When stateful `create_file` or `edit_file` tools author files during a response, the assistant message shows a compact **Workspace changes** row. Expand it to review the unique changed paths and download each file through LibreChat's authenticated file flow. This is a per-message record of files authored by those tools, not a durable snapshot of the complete workspace; stateless Code Interpreter outputs continue using the regular inline attachment interface.

### File Search

The File Search capability enables:

- RAG (Retrieval-Augmented Generation) functionality
- Semantic search across uploaded documents
- Context-aware responses based on file contents
- File attachment support at both agent and chat thread levels

### File Context

The File Context capability allows your agent to store extracted text from files as part of its system instructions:

- Extract text while maintaining document structure and formatting
- Process complex layouts including multi-column text and mixed content
- Handle tables, equations, and other specialized content
- Work with multilingual content
- Text is stored in the agent's instructions in the database
- **No OCR service required** - Uses text parsing by default with fallback methods
- **Enhanced by OCR** - If OCR is configured, extraction quality improves for images and scanned documents
- Uses the same processing logic as "Upload as Text": **OCR > STT > text parsing**
- [More info about OCR configuration](/docs/features/ocr)

**Note:** File Context includes extracted text in the agent's system instructions. For temporary document questions in individual conversations, use [Upload as Text](/docs/features/upload_as_text) from the chat instead.

### Model Context Protocol (MCP)

MCP is an open protocol that standardizes how applications provide context to Large Language Models (LLMs), acting like a universal adapter for AI tools and data sources.

For more information, see documentation on [MCP](/docs/features/mcp).

#### Agents with MCP Tools

1. Configure MCP servers in your `librechat.yaml` file
2. Restart LibreChat to initialize the connections
3. Create or edit an agent
4. Open **Add tools** in the Agent Builder and select **MCP**
5. Select the MCP server(s) you want to add; each server appears as a single entry
6. Save your changes to the agent

In this example, we've added the **[Spotify MCP server](https://github.com/LibreChat-AI/spotify-mcp)** to an agent.

![Agents - MCP](/images/agents/mcp_tools_v2.png)

#### Managing MCP Tools

Once an MCP server is added to an agent, you can fine-tune which specific tools are available:

- After adding an MCP server, expand it to see all available tools
- Check/uncheck individual tools as needed
- For example, the Spotify MCP server provides ~20 tools (search, playback control, playlist management, etc.)
- This granular control lets you limit agents to only the tools they need

Request-scoped servers that use `{{LIBRECHAT_BODY_*}}` cannot enumerate tools until a chat run supplies those values. Agent Builder therefore shows one runtime-tools selection for the complete server instead of per-tool toggles; clearing that selection detaches the server. After OAuth, LibreChat keeps the authorization and waits until the first eligible run to connect.

If an Agent explicitly selects MCP tools but none can be resolved when a run starts, LibreChat stops the run with connection and access guidance instead of silently continuing without them. Reconnect or authenticate the server, verify tool access, and retry the run.

![Agents - MCP Tools](/images/agents/mcp_agent_tools.png)

Learn More:

- [Configuring MCP Servers](/docs/features/mcp)
- [Model Context Protocol Introduction](https://modelcontextprotocol.io/introduction)

### Deferred Tools

Deferred tools allow agents to have access to many MCP tools without loading them all into the LLM context upfront. Instead, deferred tools are discoverable at runtime via a **Tool Search** mechanism.

This is especially useful when an agent has access to many MCP servers with dozens of tools—loading them all would consume a large portion of the context window and degrade response quality.

**How it works:**

- Tools marked as "deferred" are excluded from the initial LLM context
- A `ToolSearch` tool is automatically added, allowing the LLM to discover and load deferred tools on demand
- Once discovered, the tool is available for the rest of the conversation

**Configuring deferred tools:**

1. Open the Agent Builder and add MCP tools
2. Click the dropdown on any MCP tool
3. Toggle "Defer Loading" — deferred tools show a clock icon

**Note:** The `deferred_tools` capability is enabled by default. It can be toggled via the [`librechat.yaml` agents configuration](/docs/configuration/librechat_yaml/object_structure/agents#capabilities).

### Activity Groups

Activity groups collapse each contiguous block of Agent reasoning and tool calls under a generated one-line header, making long runs easier to scan. Administrators enable them with `activityLabel` and can use a faster model, another configured endpoint, a custom prompt, and per-run limits. Header generation is a separate model call whose tokens and cost are recorded. See [Agent Activity Groups](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#agent-activity-groups).

Administrators can independently enable `activityPhaseLabel` to wrap two or more logical activities in a collapsed parent summary before the final answer. This gives long runs a higher-level outline while preserving each child activity. Phase summaries use their own model, endpoint, prompt, and per-run cap, with fallback to the activity-label and run settings.

As soon as a contiguous run contains at least two completed labeled tool groups, LibreChat folds them into the same phase-card surface even before an optional phase summary arrives. The newest completed child label acts as the interim header while live reasoning and the tool call still in progress remain outside the fold. Loaded history uses the same grouping, and a later server-generated phase summary replaces the interim header without closing a card the reader opened.

When [Smooth Streaming](/docs/features/smooth_streaming) is enabled, a newly generated parent summary smoothly folds the visible activities into the collapsed group. Loaded history does not replay this transition, and LibreChat respects the device's reduced-motion preference.

Short progress text remains inside its parent phase. When the Agent begins a substantial answer, LibreChat closes the phase before that text so the user-facing result stays outside the collapsed summary. One run can create multiple parent phases when later reasoning or tools begin another logical activity block. Persisted phase boundaries are rebased when malformed or omitted content parts are compacted, so the grouping remains aligned after reload.

Files produced inside a phase remain visible in a media row beneath the collapsed card instead of being hidden inside it. Markdown image references using a bare filename, `/mnt/data/...`, or `sandbox:` path resolve to matching turn attachments. A file is omitted from the media row only when an inline image outside the collapsed phase resolves successfully, preventing duplicates without trusting broken references.

Completed tool cards show the elapsed wall-clock duration recorded for the run step. Stopped, cancelled, failed, and completed steps retain distinct statuses, including after a conversation reload.

While a tool is running, expanded Bash, Execute Code, and File Authoring detail panes follow streamed commands, code, or preview content to the bottom. Scrolling upward pauses that follow behavior so the current reading position is preserved.

### Live Reasoning Labels

Live reasoning labels replace a generic **Thinking** or **Thoughts** heading with a short orientation that evolves as sufficiently long top-level reasoning streams. The label updates the existing reasoning heading in place; it does not add or reorder message parts.

The latest live reasoning label and response timer shimmer while generation is active. Settled reasoning, older sibling responses, and labels no longer at the live tail remain static.

Administrators enable this with `reasoningLabel` and can choose a separate model, endpoint, prompt, update thresholds, and per-run call cap. Each revision is an additional model call whose tokens and cost are recorded. See [Live Reasoning Labels](/docs/configuration/librechat_yaml/object_structure/shared_endpoint_settings#live-reasoning-labels) for defaults, routing precedence, and privacy considerations.

### Tool Intent Labels

Tool intent labels replace generic live tool statuses with a short description written by the model for that specific call. Administrators must add the opt-in `tool_intents` [agent capability](/docs/configuration/librechat_yaml/object_structure/agents#capabilities). Native tools use labels automatically; in the Agent Builder, expand an MCP server and turn on **Intent label** for individual tools or the full server.

For model specs, [`describeIntent`](/docs/configuration/librechat_yaml/object_structure/model_specs#describeintent) can enable every eligible tool or select resolved tool IDs. Intent fields add to the tool schema sent to the model, so selective enablement can reduce schema-token overhead.

Intent labels do not apply to programmatic-only MCP tools because calls made inside sandbox code do not render individual tool cards. An expanded programmatic execution instead shows a compact live trace of its inner calls.

### Programmatic Tool Calling

Programmatic Tool Calling (PTC) lets agents execute selected MCP tools from inside the [Code Interpreter](/docs/features/code_interpreter) sandbox instead of calling each tool directly from the model. The model receives a Code Interpreter-backed orchestration tool, writes sandboxed code that calls generated tool functions, and can use loops, conditionals, retries, and result processing before returning an answer.

This is useful for multi-step tool workflows such as querying several resources, paginating through results, comparing outputs, or transforming tool responses before the agent explains them.

**Configuring programmatic tools:**

1. Enable both `programmatic_tools` and `execute_code` in the [`librechat.yaml` agents capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities).
2. Enable Code Interpreter on the agent.
3. Add MCP tools, expand an MCP server, and toggle **Programmatic** for individual tools or for the full server. Programmatic tools show a code icon.

The Agent Builder keeps Programmatic toggles disabled until Code Interpreter is selected. Removing Code Interpreter immediately clears the agent's Programmatic selections. On create, update, duplicate, and version restore, the server also strips programmatic caller options whenever the `execute_code` capability or Code Interpreter tool is unavailable; this lets older inconsistent Agents be opened and corrected without preserving an unusable configuration.

**Note:** `programmatic_tools` is opt-in and is not included in the default agents capability list. PTC also requires a Code Interpreter deployment with the Tool Call Server component. At runtime, LibreChat intersects the current caller-capability projection with its trusted tool registry, so event data can narrow the available map but cannot grant a tool. Only active, registered programmatic tools can be called from the sandbox; unregistered calls are rejected.

Expand the Code Interpreter card during a programmatic run to see a terminal-style trace of each inner tool call, its argument preview, status, duration, and any failure. The trace is live for the current browser session and is not restored after a page reload.

### Background Tool Calls

Background tool calls let an agent start a long-running eligible Code Interpreter, MCP, Plugin, or Action tool and keep working. The initial call returns immediately with a task reference. For a saved Agent, supported completions are delivered automatically in a follow-up continuation by default; `check_background_task` remains available for explicit status, control, artifact collection, and recovery.

To use this feature, an administrator must add the opt-in `run_in_background` [agent capability](/docs/configuration/librechat_yaml/object_structure/agents#capabilities). Then configure the eligible tools in the Agent Builder:

- **Code Interpreter:** Enable Code Interpreter on the agent. Code execution and shell commands become background-eligible by default; turn off **Background execution** in its tool settings to opt out.
- **MCP:** Open an MCP server and mark individual tools, or the full server, as **Background**.
- **Plugin tools:** Open an eligible tool and turn on **Background execution**.
- **Actions:** Turn on **Background execution** for an Action to opt in every eligible operation in that Action. OAuth Actions do not expose this switch. An operation is also excluded when its own schema already defines a `run_in_background` parameter, avoiding a collision with LibreChat's dispatch flag.

These settings allow background execution but do not force it. The model decides per call whether to run an eligible tool in the background.

In LibreChat chat, when background code finishes, its stdout and generated files appear on the original code call rather than the later poll. Generated files are also persisted for subsequent turns. Automatic continuations present the completed work in a collapsible wake-up task card linked to the original task.

When the server starts that continuation after the preceding foreground run has already finished, the open conversation attaches to the new generation and refreshes the completed task output without requiring a reload.

Automatic completion wakeups are enabled unless an administrator sets [`endpoints.agents.backgroundTasks.completionWakeups: false`](/docs/configuration/librechat_yaml/object_structure/agents#backgroundtasks), which restores poll-only behavior. The host may batch several eligible content-only completions into one continuation. Tasks that produce a live artifact still require polling on the process that owns the run.

Only eligible tools expose the setting. Ordinary background execution is process-local and does not survive the loss of its worker process. Once a content-only terminal result is persisted, however, automatic delivery is durable and may continue on another replica. The process-local registry bounds running tasks and retained result payloads per conversation, user, and process; it evicts settled work when safe and returns a scoped capacity result when pending processing prevents safe eviction. If an invocation ignores cancellation, LibreChat stops renewing its producer lease after a one-minute post-abort grace period and retires automatic completion delivery without falsely marking uncertain external work as timed out. Detached Subagents use a separate durable task and transcript path with Redis-routed live controls; see [Detached Subagent Threads](/docs/features/subagents#detached-subagent-threads).

### Ask User and Tool Approval

The **Ask User** tool lets an agent pause a run to request missing information, present choices, or confirm how to proceed. One call can group up to four related questions. LibreChat presents one question at a time with Back, Next, and clickable progress steps; answers remain available while moving between steps, and single-select answers advance automatically without submitting. Submit appears on the final step after every question has an answer, while Skip declines the whole batch from any step. Each question accepts a free-form answer or the agent's single- or multi-select choices. While the model prepares the call, LibreChat streams the questions into a progress card; the interactive form replaces it when ready. After one submission, the same run resumes with the complete answer map. Add **Ask User** from the Tools marketplace; administrators can remove the default `ask_user_question` capability to hide it.

Collapsing a multi-question card returns the composer to its normal steer-or-queue mode while the paused card remains in the thread. The card can still be expanded, answered, skipped, or stopped. A single-question pause continues to use the focused composer answer mode.

Submitted answers are persisted with the resume action, so stopping while the resumed run initializes does not discard accepted answers.

After a question record settles, it collapses to a one-line tool entry by default so long answers do not dominate the transcript. Open it to review the questions and preserved line breaks in the answers. The existing **Auto-expand tool details** preference also controls this settled view; the live form remains open while input is still required.

Each question accepts an optional heading up to 80 characters, question text up to 2,000 characters, optional supporting context up to 4,000 characters, and up to 12 choices. Choice labels are limited to 280 characters, choice values to 500 characters, and each submitted answer to 16,000 characters.

Administrators can also require review before matching tool calls run. Depending on the tool, users can approve, reject, edit arguments, or respond directly. Paused runs are checkpointed so they can resume after the decision, including on another replica when the default MongoDB checkpointer is used. Deployment-wide tool approval is disabled by default, while an Agent using an attached Code environment receives the [ask-by-default safety policy](/docs/features/code_interpreter#attached-environment-tool-permissions) unless an administrator explicitly disables it. See [`toolApproval` and `checkpointer`](/docs/configuration/librechat_yaml/object_structure/agents#toolapproval).

### Skills

Skills let agents load reusable instructions from `SKILL.md` definitions. They can be selected manually from chat with `$`, automatically discovered by the model through the skill catalog, or always applied on every turn. The Agent Builder can also enable standalone runtime authoring: an agent may create a Skill without receiving the existing catalog, or edit selected Skills when the user has the corresponding permission.

For authoring, invocation, and access control details, see [Skills](/docs/features/skills).

### Memory

The **Memory** tool lets an agent save, update, or delete structured user memories when the user explicitly asks it to remember or forget something. Memory must be configured for the deployment, allowed by the user's role and personalization settings, and enabled on the agent.

By default, an agent uses the user's shared personal memory pool. Turn on **Keep memories separate for this agent** to give it a private per-user, per-agent partition. The isolated agent will not see existing shared memories or memories created by other isolated agents. Users can filter the Memory panel by personal or agent-specific memories. Reading, creating, or updating an Agent partition requires current access to that Agent; deletion can still clean up a partition after its Agent has been removed. See [User Memory](/docs/features/memory#agent-memory).

### Artifacts

The Artifacts capability enables your agent to generate and display interactive content:

- Create React components, HTML code, and Mermaid diagrams
- Display content in a separate UI window for clarity and interaction
- Configure artifact-specific instructions at the agent level
- [More info about Artifacts](/docs/features/artifacts)

When enabled, choose one of three instruction modes:

- **Normal**: Adds the standard artifact instructions for React, HTML, SVG, Markdown, and Mermaid.
- **shadcn/ui**: Adds the standard instructions plus guidance for building interfaces with the shadcn/ui component library.
- **Custom**: Keeps artifact rendering enabled but injects no built-in artifact instructions, giving the agent's own instructions full control.

Configuring artifacts at the agent level is the preferred approach, as it allows for more granular control compared to the legacy app-wide configuration.

If you select **Custom**, include at minimum the basic artifact format in your instructions.

Here's a simple example of the minimum instructions needed:

````md
When creating content that should be displayed as an artifact, use the following format:

:::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"}

```
Your artifact content here
```

:::

For the type attribute, use one of:

- "text/html" for HTML content
- "application/vnd.mermaid" for Mermaid diagrams
- "application/vnd.react" for React components
- "image/svg+xml" for SVG images
````

### Tools

Agents can also be enhanced with various built-in tools:

- **[OpenAI Image Tools](/docs/features/image_gen#openai-image-tools)**: Image generation & editing using **[GPT-Image-1](https://platform.openai.com/docs/models/gpt-image-1)**
- **[Gemini Image Tools](/docs/configuration/tools/gemini_image_gen)**: Image generation and image-context editing using Gemini image models
- **[DALL-E-3](/docs/features/image_gen#dalle-legacy)**: Image generation from text descriptions
- **[Stable Diffusion](/docs/features/image_gen#stable-diffusion-local)** / **[Flux](/docs/features/image_gen#flux)**: Text-to-image generation
- **[Wolfram](/docs/configuration/tools/wolfram)**: Computational and mathematical capabilities
- **[OpenWeather](/docs/configuration/tools/openweather)**: Weather data retrieval
- **[Google Search](/docs/configuration/tools/google_search)**: Access to web search functionality
- **[Calculator](/docs/configuration/tools/calculator)**: Mathematical calculations
- **[Tavily Search](/docs/configuration/tools/tavily)**: Advanced search API with diverse data source integration
- **[Azure AI Search](/docs/configuration/tools/azure_ai_search)**: Information retrieval from Azure AI Search indexes
- **[Traversaal](/docs/configuration/tools/traversaal)**: A robust search API for LLM Agents

#### Create an Agent with Image Tools

1. Add the image tool credentials to `.env`, such as `IMAGE_GEN_OAI_API_KEY` for OpenAI Image Tools.
2. Restart LibreChat so the new environment variables are loaded.
3. Select **Agents** from the endpoint menu.
4. Open the Agent Builder from the side panel and create or edit an agent.
5. Open the agent's **Tools** list, select **OpenAI Image Tools**, **Gemini Image Tools**, **DALL-E-3**, **Stable Diffusion**, or **Flux**, then save the agent.
6. Start a chat with that agent and ask it to generate or edit an image.

For the full image setup guide, including model variables such as `IMAGE_GEN_OAI_MODEL`, see [Image Generation & Editing](/docs/features/image_gen).

- Tools can be disabled using the [`librechat.yaml`](/docs/configuration/librechat_yaml) configuration file:
  - [More info](/docs/configuration/librechat_yaml/object_structure/agents#capabilities)

### Actions

With the Actions capability, you can dynamically create tools from [OpenAPI specs](https://swagger.io/specification/) to add to your Agents.

![Agents - Endpoints Menu](/images/agents/actions.png)

**Clicking the button above will open a form where you can input the OpenAPI spec URL and create an action:**

![Agents - Endpoints Menu](/images/agents/actions_panel.png)

- Actions can be disabled using the [`librechat.yaml`](/docs/configuration/librechat_yaml) configuration file:
  - [More info](/docs/configuration/librechat_yaml/object_structure/agents#capabilities)
- Individual domains can be whitelisted for agent actions:
  - [More info](/docs/configuration/librechat_yaml/object_structure/actions#alloweddomains)
- Note that you can add add the 'x-strict': true flag at operation-level in the OpenAPI spec for actions.
  If using an OpenAI model supporting it, this will automatically generate function calls with 'strict' mode enabled.
  - Strict mode supports only a partial subset of json. Read https://platform.openai.com/docs/guides/structured-outputs for details.

### Handoffs

Handoffs let a primary agent transfer a conversation to a specialist agent with the relevant context. In **Advanced settings**, open the orchestration section and add up to 10 handoff agents. Each configured handoff creates a transfer tool that the model can choose dynamically when the specialist's expertise is needed.

For each handoff, you can add a description that helps the model choose the right specialist. You can also provide passthrough instructions describing what content the primary agent should generate for the specialist and optionally rename the content parameter from its default, `instructions`. Handoffs are always available in the Agent Builder; unlike Agent Chain and Subagents, they do not require an endpoint capability to be enabled. Tool calls from reachable handoff agents remain structured in later turns, so providers receive valid tool-call history instead of flattened text.

### Agent Chain

The Agent Chain capability enables a Mixture-of-Agents (MoA) approach, allowing you to create a sequence of agents that work together:

- Create chains of specialized agents for complex tasks
- Each agent in the chain can access outputs from previous agents
- Configure the maximum number of steps for the agent chain
- **Note:** Access this feature from the Advanced Settings panel in the Agent Builder
- **Note:** This feature is currently in beta and may be subject to change
  - The current maximum of agents that can be chained is 10, but this may be configurable in the future

<img
  src="https://firebasestorage.googleapis.com/v0/b/superb-reporter-407417.appspot.com/o/agent_chain.png?alt=media&token=bfa209b9-d2ab-403f-b097-6300b4017fc8"
  alt="Agent Chain"
  width={488}
  height={269}
/>

This feature introduces a layered Mixture-of-Agents architecture to LibreChat, where each agent takes all the outputs from agents in the previous layer as auxiliary information in generating its response, as described in [the eponymous "Mixture-of-Agents" paper](https://arxiv.org/abs/2406.04692).

### Subagents

Subagents let an agent delegate a focused task to an isolated child run or a saved Agent team. Each child gets isolated context and tool execution, then returns a compact result to the parent instead of filling the parent context with every intermediate step. Detached child work is also preserved as a navigable, view-only thread.

Subagents are different from Agent Chain: chain runs coordinate multiple agents as graph participants, while subagents are spawned by an agent as a tool call for scoped delegation. LibreChat preserves the structured tool-call history needed by reachable children and saved-team members across later turns while keeping unavailable tools out of the active run. For setup and limits, see [Subagents](/docs/features/subagents).

### Advanced Settings

Advanced settings for your agent (found in the Advanced view of the Agent form) outside of "capabilities."

#### Max Agent Steps

This setting allows you to limit the number of steps an agent can take in a "run," which refers to the agent loop before a final response is given.

If left unconfigured, the default is 25 steps, but you can adjust this to suit your needs. For admins, you can set a global default as well as a global maximum in the [`librechat.yaml`](/docs/configuration/librechat_yaml/object_structure/agents#recursionlimit) file.

When a turn uses its complete step budget, LibreChat preserves the partial response and tool calls instead of replacing them with an error. The message offers **Keep going**, which starts a new turn with a fresh tool budget, and **Answer now**, which asks the agent to answer from the work already completed without more tools. Dismissing the notice affects only the current browser view.

Administrators can also bound streamed tool-call arguments and model delta events with [Agent stream circuit breakers](/docs/configuration/librechat_yaml/object_structure/agents#stream-circuit-breakers).

A "step" refers to either an AI API request or a round of tool usage (1 or many tools, depending on how many tool calls the LLM provides from a single request).

A single, non-tool response is 1 step. A singular round of tool usage is usually 3 steps:

1. API Request -> 2. Tool Usage (1 or many tools) -> 3. Follow-up API Request

## File Management

Agents support multiple ways to work with files:

### In Chat Interface

When chatting with an agent, you have four upload options:

1. **Upload Images**
   - Uploads images for native vision model support
   - Sends images directly to the model provider

2. **Upload as Text** (requires `context` capability)
   - Extracts and includes full document content in conversation
   - Uses text parsing by default; enhanced by OCR if configured
   - Content exists only in current conversation
   - See [Upload as Text](/docs/features/upload_as_text)

3. **Upload for File Search** (requires `file_search` capability, toggled ON)
   - Uses semantic search (RAG) with vector stores
   - Returns relevant chunks via tool use
   - Optimal for large documents/multiple files
   - Sub-optimal for structured data (CSV, Excel, JSON, etc.)

4. **Upload for Code Interpreter** (requires `execute_code` capability, toggled ON)
   - Adds files to code interpreter environment
   - Optimal for structured data (CSV, Excel, JSON, etc.)
   - More info about [Code Interpreter](/docs/features/code_interpreter)

### In Agent Builder

When configuring an agent, you can attach files in different categories:

1. **Image Upload**: For visual content the agent can reference
2. **File Search Upload**: Documents for RAG capabilities
3. **Code Interpreter Upload**: Files for code processing
4. **File Context**: Documents with extracted text to supplement agent instructions

**File Context** uses the `context` capability and works just like ["Upload as Text"](/docs/features/upload_as_text) - it uses text parsing by default and is enhanced by OCR when configured. Text is extracted at upload time and stored in the agent's instructions. This is ideal for giving agents persistent knowledge from documents, PDFs, code files, or images with text.

**Processing priority:** OCR > STT > text parsing (same as Upload as Text)

**Note:** The extracted text is included as part of the agent's system instructions.

## Sharing and Permissions

Agents use LibreChat's granular [access control](/docs/features/access_control) system. Each agent has its own Access Control List (ACL), and can be shared with specific **users**, **groups**, **roles**, or **publicly**, each at a chosen permission level.

### Access Roles

When sharing an agent, the grantee is assigned one of three roles:

| Role       | What the grantee can do                                                                               |
| ---------- | ----------------------------------------------------------------------------------------------------- |
| **Viewer** | Use the agent in conversations; cannot open the builder or see instructions, tools, or attached files |
| **Editor** | View + modify the agent's instructions, model, tools, and files                                       |
| **Owner**  | Full control: view, edit, delete, and re-share the agent                                              |

The original author and administrators always retain full control regardless of the ACL.

### Sharing an Agent

1. Open the agent in the Agent Builder
2. Click the **Share** button in the footer (visible when you're the author, an admin, or have been granted `SHARE` permission on that specific agent)
3. Search for users, groups, or roles in the people picker and assign each a role
4. Optionally toggle **Public** to make the agent visible to everyone on the instance (requires the `SHARE_PUBLIC` feature permission)

Agent cards and landing views show the configured support contact. When none is set, LibreChat can fall back to the first resolvable owner's display name, but does not expose the owner's email address unless it was configured explicitly as the support contact.

Conversation surfaces keep the Agent as the visible identity and do not fall back to its backing model or model-spec label.

For full details on principals, permission bits, and how ACLs compose with role-based feature permissions, see [Access Control](/docs/features/access_control).

### Administrator Controls

Administrators have access to global permission settings within the agent builder UI:

- Enable/disable agent sharing across all users
- Control agent usage permissions
- Manage agent creation rights
- Configure platform-wide settings

In an unscoped single-tenant deployment, the first account created for the instance is an administrator. Tenant-scoped deployments do not auto-promote their first registered user; provision tenant administrators through a trusted administrative flow. If you need to add an administrator manually, you may [access MongoDB](/docs/configuration/mongodb/mongodb_auth) and update the user's profile:

```
db.users.updateOne(
  { email: 'USER_EMAIL_ADDRESS' },
  { $set: { role: 'ADMIN' } }
)
```

The use of agents for all users can also be disabled via config, [more info](/docs/configuration/librechat_yaml/object_structure/interface).

Feature-level permissions (who can _use_, _create_, _share_, or _share publicly_ agents) are managed from the [**LibreChat Admin Panel**](/docs/features/admin_panel) on each role, including any custom roles. The [`interface.agents`](/docs/configuration/librechat_yaml/object_structure/interface#agents) block in `librechat.yaml` can still seed defaults for the built-in `USER` role at startup, but the admin panel is the recommended way to edit them going forward.

### User-Level Sharing

Individual users can:

- Share their agents with specific users, groups, or roles (if `SHARE` is enabled for their role)
- Make agents visible to everyone on the instance (if `SHARE_PUBLIC` is enabled)
- Grant each recipient a different access level (Viewer / Editor / Owner)
- Re-share or revoke access at any time from the share dialog

## Steering and Queued Messages

While an Agent is responding, you can send another message in either of two ways:

- **Steer** inserts the message into the current run at its next tool or agent step, so the agent can adjust its work before finishing.
- **Queue** holds the message and sends it as a normal follow-up turn after the current response completes.

For saved Agent conversations on a current server, ordinary queued follow-ups are stored in MongoDB. LibreChat preserves their FIFO order and carries their text, attachments, quoted excerpts, and manually selected Skills across browser disconnects, app restarts, and replica handoffs. Processing waits for the preceding response to settle before admitting the next turn. This durability is automatic and has no YAML or environment toggle; older servers and short startup windows without an active generation epoch retain the client-side compatibility queue.

Server admission is serialized within each conversation queue lane. If predecessor evidence or mixed-version ownership is ambiguous, LibreChat shows **Awaiting reconciliation** and blocks or fails the affected queued turn instead of guessing and starting it out of order.

**Interrupt & steer** preempts the current provider work. If no answer text or tool activity is safe to keep yet, LibreChat waits through a short grace period, discards the silent or reasoning-only attempt, inserts the message, and restarts the model with that instruction. Once answer text can be kept, LibreChat stops at a provider-safe boundary, preserves the partial response, inserts the message, and resumes the same assistant response. A running tool call is not discarded or interrupted by steering; the message applies when that work reaches a safe boundary.

Use **Stop** to end active reasoning and request cancellation of a foreground tool call. Stop forwards cancellation to signal-aware foreground tools; detached background work keeps its independent lifecycle. Use the dedicated composer control, choose it from the send-button menu, or press `Command/Ctrl + Shift + .`. The action falls back to ordinary steering when the deployment cannot interrupt the active provider stream.

Under **Settings → Chat**, **While generating, Enter will** chooses the default action. The send-button menu can override that choice for an individual message, and **Steering interrupts generation** controls whether ordinary steering also requests an interrupt.

Files and [quoted excerpts](/docs/features/message_actions#quote-excerpts) travel with steer, queue, and interrupt messages. Manually selected Skills remain staged for the next full turn instead of being attached to a mid-run steer.

Pending steers appear above the composer until the server inserts them into the run. Their receipt progresses from **Sending** to **Delivered**, then **Interrupting** when a preempt is armed, and finally **Applied** with a double checkmark at the inline message's bottom-right edge. Confirmed applied receipts remain visible after reload and in share or search views; uncertain or failed delivery never shows a confirming checkmark. Once acknowledged, the message menu can reclaim the steer for editing, convert it into a queued follow-up, or cancel it and restore its text and attachments to the composer. LibreChat avoids overwriting a newer draft; if the composer is no longer available, it preserves the reclaimed message in the queue instead. A failed steer remains available to retry, edit, queue, or remove.

Queued follow-ups can be sent immediately, converted into a steer while the run is still active, escalated to **Interrupt & steer now**, edited, or removed. In-flight steers offer the same escalation when they are still waiting for a tool boundary. Removing a server-backed queued message first cancels its durable source, then restores it to an empty composer when possible. If the preceding response is aborted or fails, LibreChat marks the affected queued turn as failed for review instead of silently sending it.

A conversation can have up to 100 active queued turns. Each queued turn accepts up to 16,000 characters and 10 files. Separately, one active run accepts up to 10 pending steers; each steer can include up to 10 files and is limited by [`STEER_MAX_LENGTH`](/docs/configuration/dotenv#agent-conversation-controls). **Interrupt & steer** continues to use the live steering path rather than moving a queued turn to the front of the durable FIFO lane.

Redis-backed multi-replica deployments negotiate generation protocol v2 automatically. Follow the [generation protocol compatibility guidance](/docs/configuration/redis#generation-protocol-compatibility) when upgrading from a release older than `v0.8.8-rc1`.

### Notes

- Instructions, model parameters, attached files, and tools are only exposed to the user if they have editing permissions
  - An agent may leak any attached data, whether instructions or files, through conversation, so make sure your instructions are robust against this before granting Editor/Owner access or making the agent public
- Only original authors, administrators, and grantees with Owner permission can delete shared agents
- Agents are private to authors unless shared

## Agent Event Delivery

LibreChat includes a source-neutral delivery layer for asynchronous Agent events. Deployment-owned adapters can normalize a verified webhook, queue message, MCP event, channel event, or internal product event into a new Agent run (`fire`), a new turn on an existing saved Agent and branch (`continue`), or a mutation of an active run (`steer`). Adapters remain responsible for authenticating their native source, sanitizing payloads, and selecting trusted targets before enqueueing.

The beta [Agents API event endpoints](/docs/features/agents_api#agent-events) expose authenticated `fire`, `continue`, and `steer` admission to Remote Agents API keys. External systems can bind a source actor to a direct child Agent and continue that actor across events without an additional feature toggle. LibreChat derives the user, tenant, and source identity from the API key, enforces Agent access and content filters, and requires an idempotency key. This is not an unauthenticated general-purpose webhook receiver; provider-specific adapters must still verify native webhook signatures before calling it.

The experimental [Scheduled Chats](/docs/features/scheduled_chats) feature is LibreChat's first built-in producer for this layer. It creates recurring `fire` deliveries for saved Agents and is disabled until an administrator opts in. Detached Subagents automatically use idempotent `continue` delivery to wake a saved parent Agent after a child settles; cancelled children and ephemeral parent Agents are excluded.

MongoDB stores bounded versioned envelopes, idempotency keys, ordering lanes, leases, retry history, durable receipts, and dead letters. Workers use at-least-once delivery with fenced claims and bounded exponential backoff; successful receipts expire after 90 days, while dead letters require an explicit trusted requeue. Fire deliveries create a new conversation. Continue deliveries create a new turn on the exact conversation branch and defer while that branch is running, paused, or finalizing. Bound actors also have an automatic mailbox: one actor waits for its current turn's handling outcome before dispatching the next event, while different actors can run in parallel. Steer deliveries must identify the existing conversation and generation and use strict admission so ambiguous retries cannot inject the same instruction twice.

For bound continuations, transport success starts a separate durable handling lifecycle. A delivery progresses from `started` to `applied`, `completed_no_action`, `failed`, or `cancelled`. Sources can require an exact tool and optional argument subset with `expectedAction`; LibreChat reports `applied` only from host-observed matching tool evidence, never from model-written prose. Compatible observational events can share a bounded `coalesce.key` batch while retaining one idempotency record and receipt per source event. Checkpoint continuation is used when compatible and otherwise falls back to durable message history.

Bound Event Actors can pause for Ask User or tool approval and resume the exact signed actor invocation through LibreChat's normal human-in-the-loop interface. The actor's mailbox remains blocked until that pause resumes or settles, preventing a later event from overtaking the decision. These actors require the durable MongoDB checkpointer. LibreChat selects checkpoint continuation or durable-history reconstruction automatically from the actor state and request capability; existing protocol-v1 work remains on the history path until it drains.

Detached Actions started by Event Actors are enabled automatically when the built-in generation store advertises support. The actor remains suspended until terminal evidence resumes its original invocation, so a background launch alone does not satisfy `expectedAction`. The in-memory store keeps this lifecycle coherent while its process remains alive. Redis adds durable restart recovery and replica handoff. Capability-owned completion work is isolated from older workers during a mixed-version drain; operators do not select this behavior with a feature flag.

The execution host normally calls the current process's bound listener. Configure [`endpoints.agents.eventDriven.selfUrl`](/docs/configuration/librechat_yaml/object_structure/agents#eventdriven) only when this trusted internal admission path must traverse another HTTP origin, such as a TLS front door.

## Optional Configuration

LibreChat allows admins to configure the use of agents via the [`librechat.yaml`](/docs/configuration/librechat_yaml) file:

- Disable Agents for all users (including admins): [more info](/docs/configuration/librechat_yaml/object_structure/interface)
- Customize agent capabilities using: [more info](/docs/configuration/librechat_yaml/object_structure/agents)

## Best Practices

- Provide clear, specific instructions for your agent
- Carefully consider which tools are necessary for your use case
- Organize files appropriately across the four upload categories
- Review permission settings before sharing agents
- Test your agent thoroughly before deploying to other users

## Recap

1. Select "Agents" from the endpoint dropdown menu
2. Open the Agent Builder panel
3. Fill out the required agent details
4. Configure desired capabilities (Code Interpreter, File Search, File Context, etc.)
5. Add necessary tools and files
6. Set sharing permissions if desired
7. Create and start using your agent

When chatting with agents, you can:

- Use "Upload as Text" to include full document content in conversations (text parsing by default, enhanced by OCR)
- Use "Upload for File Search" for semantic search over documents (requires RAG API)
- Add files to agent's "File Context" to included a file's full content as part of the agent's system instructions

## Migration Required (v0.8.0-rc3+)

<Callout type="warning" title="Important: Agent Permissions Migration Required">
  Starting from version v0.8.0-rc3, LibreChat uses a new Access Control List (ACL) based permission
  system for agents. If you're upgrading from an earlier version, you must run the agent permissions
  migration for existing agents to remain accessible.
</Callout>

### What the Migration Does

The agent permissions migration transitions your agents from a simple ownership model to a sophisticated ACL-based system with multiple permission levels:

- **OWNER**: Full control over the agent
- **EDITOR**: Can view and modify the agent
- **VIEWER**: Read-only access to the agent

Without running this migration, existing agents will be inaccessible through the new permission-aware API endpoints.

### Running the Migration

Choose the appropriate command based on your deployment method:

#### 1. For the default `docker-compose.yml` (if you use `docker compose up` to start the app):

**Preview changes (dry run):**

```bash
docker compose exec api npm run migrate:agent-permissions:dry-run
```

**Execute migration:**

```bash
docker compose exec api npm run migrate:agent-permissions
```

**Custom batch size (for large datasets):**

```bash
docker compose exec api npm run migrate:agent-permissions:batch
```

#### 2. For the `deploy-compose.yml`

If you followed the [Ubuntu Docker Guide](/docs/remote/docker_linux):

**Preview changes (dry run):**

```bash
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run migrate:agent-permissions:dry-run"
```

**Execute migration:**

```bash
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run migrate:agent-permissions"
```

**Custom batch size (for large datasets):**

```bash
docker exec -it LibreChat-API /bin/sh -c "cd .. && npm run migrate:agent-permissions:batch"
```

#### 3. For local development (from project root):

**Preview changes (dry run):**

```bash
npm run migrate:agent-permissions:dry-run
```

**Execute migration:**

```bash
npm run migrate:agent-permissions
```

**Custom batch size (for large datasets):**

```bash
npm run migrate:agent-permissions:batch
```

### What Happens During Migration

- **Private Agents**: Remain accessible only to their creators (receive OWNER permission)
- **Shared Agents**: If an agent was previously shared, it will receive appropriate ACL entries as a Public Agent (shared to all users)
- **System Detection**: LibreChat automatically detects unmigrated agents at startup and displays a warning

You can adjust the resulting agent permissions via the Agent Builder UI.

<Callout type="info" title="Note">
  The same migration process applies to prompts. If you also have existing prompts, run the prompt
  permissions migration using the same commands but replace `agent` with `prompt` in the command
  names.
</Callout>

## Agents API (Beta)

LibreChat agents can also be accessed programmatically via API, enabling external applications and scripts to interact with your agents using OpenAI-compatible SDKs or the Open Responses format.

See the [Agents API documentation](/docs/features/agents_api) for setup and usage details.

## What's next?

LibreChat Agents usher in a new era for the app where future pipelines can be streamlined via Agents for specific tasks and workflows across your experience in LibreChat.

Future updates will include:

- General improvements to the current Agent experience
- Multi-agent orchestration for complex workflows
- Ability to customize agents for various functions: titling (chat thread naming), memory management (user context/history), and prompt enhancement (input assistance/predictions)
- More tools, configurable tool parameters, dynamic tool creation.

Furthermore, the update introduces a new paradigm for LibreChat, as its underlying architecture provides a much needed refresh for the app, optimizing both the user experience and overall app performance.

To highlight one notable optimization, an AI generation of roughly 1000 tokens will transfer about 1 MB of data using traditional endpoints (at the time of writing, any endpoint option besides Agents and AWS Bedrock).

Using an agent, the same generation will transfer about about 52 kb of data, a 95% reduction in data transfer, which is that much less of a load on the server and the user's device.

---

AI Agents in LibreChat provide a powerful way to create specialized assistants without coding knowledge while maintaining the flexibility to work with your preferred AI models and providers.


# Scheduled Chats (https://www.librechat.ai/docs/features/scheduled_chats)

Scheduled Chats let a saved LibreChat Agent start a new conversation automatically with a prompt you provide. Use them for recurring reports, reminders, research, and other Agent workflows that should run without starting each chat manually.

<Callout type="warning" title="Experimental and disabled by default">
  Scheduled Chats are in an early experimentation phase. Their behavior, configuration, and
  deployment requirements may change substantially. Scheduled runs can consume model, tool, and
  Code Interpreter resources, so LibreChat does not enable the feature unless an administrator
  explicitly opts in.
</Callout>

## Enable Scheduled Chats

Add `interface.schedules` to `librechat.yaml`, then restart LibreChat:

```yaml filename="librechat.yaml"
interface:
  schedules:
    use: true
    create: true
    maxPerUser: 10
    minIntervalMinutes: 60
    autoDisableAfterFailures: 5
    fireConcurrency: 5
    requireProject: false
    # projectId: '000000000000000000000000'
```

`use` and `create` seed the `SCHEDULES` permissions for the built-in `USER` role at startup. Administrators can manage those permissions per role in the [Admin Panel](/docs/features/admin_panel). The remaining values are runtime limits:

<OptionTable
  options={[
    ['maxPerUser', 'Number', 'Maximum schedules each user can own. Set to 0 to prevent creation.', '10'],
    ['minIntervalMinutes', 'Number', 'Shortest allowed interval between occurrences.', '60'],
    ['autoDisableAfterFailures', 'Number', 'Consecutive failed runs before a schedule is disabled.', '5'],
    ['fireConcurrency', 'Number', 'Maximum scheduled runs admitted concurrently across the deployment.', '5'],
    ['requireProject', 'Boolean', 'Requires every schedule to resolve to a Chat Project at write time and at each run.', 'false'],
    ['projectId', 'String', 'Pins scheduled conversations to one owner-scoped Chat Project and implies `requireProject`.', ''],
  ]}
/>

The entire `schedules` field is absent by default. `schedules: true` enables the feature with default limits, while `schedules: false` or `schedules: { use: false }` is a deployment-wide stop. A disabled base configuration cannot be re-enabled by a role, group, or user configuration override. `maxPerUser` counts every owned schedule, including definitions created before slot accounting was introduced. See [`interface.schedules`](/docs/configuration/librechat_yaml/object_structure/interface#schedules) for complete merge and permission behavior.

## Create and Manage a Schedule

Open **Scheduled Chats** from the side navigation, then select **New schedule**. A schedule requires:

- A name and prompt
- A saved Agent you can view and use
- An hourly, daily, weekday, weekly, or custom cron cadence
- An IANA time zone
- An optional Chat Project destination, unless the administrator requires or pins one

New schedules start with the current browser's IANA time zone. You can choose another supported IANA zone, with the browser zone and UTC pinned at the top and each option showing its current GMT offset. Existing schedules reopen with their stored zone. Browsers without `Intl.supportedValuesOf` can still use the current zone, UTC, or the schedule's stored zone. Changing only the zone is a timing change: LibreChat recomputes the next occurrence and revalidates the cadence against the deployment's minimum interval.

The structured cadence options are:

- **Hourly**: choose any minute from `00` through `59`.
- **Daily**: choose one local time.
- **Weekdays**: choose one local time from Monday through Friday.
- **Weekly**: choose one or more weekdays and one local time. At least one day is required.

Time controls follow the browser's [Clock Format preference](/docs/features/settings#general), and weekly day controls and summaries follow its **Week Starts On** preference. These preferences change presentation only; the stored IANA zone and selected weekdays determine execution.

Choose **Custom** to enter a recurring five-field cron expression in `minute hour day-of-month month day-of-week` order. Seconds and year fields are rejected, as are expressions longer than 256 characters or expressions with no future occurrence. The dialog validates and previews upcoming occurrences in the selected time zone using the same parser as the server. Examples include `0 9,17 * * 1-5` for 09:00 and 17:00 on weekdays and `0 9 1 * *` for 09:00 on the first day of each month. Every cadence must still satisfy `minIntervalMinutes`; for example, `*/15 * * * *` requires an administrator to allow intervals shorter than the default 60 minutes.

Daylight-saving transitions remain timezone-aware: nonexistent local times move to the first valid instant, and repeated local times run only once. Interval validation measures the effective cadence and zone together, including shortened real-time gaps across spring-forward transitions, so changing a zone can make an otherwise valid expression fall below the configured floor. Schedule prompts are literal text; message-composer variable insertion is not available in this form.

Each occurrence starts a new conversation with the selected Agent. Schedule cards show the next occurrence and latest status, and link to the latest conversation when one exists. Users with `SCHEDULES: CREATE` can edit, enable or disable, run immediately, and delete their schedules. **Run now** can start a disabled schedule once without re-enabling future occurrences, but it still respects permissions, deployment capacity, and the global stop.

If a run pauses for Ask User or tool approval, its status becomes **Needs approval** and the conversation can be opened to complete the decision. LibreChat prevents overlapping occurrences of the same schedule and may skip an occurrence when another run is active or the account has insufficient balance.

Schedules can disable themselves after repeated failures, or when their Agent is deleted, its configuration is invalid, access is revoked, the account lacks sufficient balance, a required project is missing, or the effective project was deleted or is not owned by the schedule owner. Project policy is rechecked at every run so tightening `requireProject` or changing a pin also applies to existing schedules. Fix the underlying issue before enabling the schedule again.

An operator-level `projectId` overrides the project stored on each schedule and implies `requireProject`. Because Chat Projects are user-owned, a global pin normally works for only that project's owner; use role or user configuration overrides when different owners need different pinned destinations.

## Permissions

`SCHEDULES: USE` controls whether a user can list and view their schedules. `SCHEDULES: CREATE` additionally gates creating, editing, enabling, running, and deleting them. Scheduled execution also rechecks the user's Agent feature permission and view access to the selected Agent at run time; losing either access disables the affected schedule.

See [Access Control](/docs/features/access_control) for role permissions and resource ACLs.

## Deployment Safety

Scheduled Chats use MongoDB leases, idempotent trigger delivery, and overlap prevention so eligible replicas can coordinate durable occurrences. Multi-replica deployments must also enable Redis-backed resumable streams with `USE_REDIS_STREAMS=true`. Without shared streams, schedule writes fail closed instead of admitting work that another replica may be unable to resume or stop.

A scheduled Agent that can pause for Ask User or tool approval requires `USE_REDIS_STREAMS=true` for shared action state and a durable shared checkpointer for graph continuation, even in an otherwise single-process deployment. The built-in default MongoDB checkpointer satisfies this requirement; `type: memory` does not. LibreChat rejects the run before model work begins when either shared store is unavailable.

A deployment that truly runs one LibreChat process without Redis can opt in with:

```bash filename=".env"
SCHEDULES_SINGLE_PROCESS=true
```

Do not set this in a multi-process or multi-replica deployment. The legacy experimental clustered server entrypoint does not arm the schedule engine; use LibreChat's standard server entrypoint with Redis-backed streams for horizontal scaling.

For an immediate deployment-wide stop, set `SCHEDULES_DISABLED=true`. This blocks automatic occurrences and **Run now** without deleting schedule definitions. See [Scheduled Chats environment variables](/docs/configuration/dotenv#scheduled-chats).


# Skills (https://www.librechat.ai/docs/features/skills)

Skills are reusable instruction bundles for LibreChat Agents. A skill is centered on a `SKILL.md` file: frontmatter describes when the skill should be used, and the markdown body gives the agent the procedure, rules, examples, or references to follow.

Skills are useful for repeatable work such as:

- Applying brand or writing guidelines
- Following internal review checklists
- Running a standard research workflow
- Priming a specialized tool workflow
- Packaging reusable scripts, references, and assets with an instruction file

## Enable Skills

The `skills` agent capability is enabled by default. Admins can remove it from the agents endpoint capability list to hide Skills from users.

```yaml filename="librechat.yaml"
endpoints:
  agents:
    capabilities:
      - 'deferred_tools'
      - 'execute_code'
      - 'file_search'
      - 'web_search'
      - 'artifacts'
      - 'subagents'
      - 'actions'
      - 'context'
      - 'skills'
      - 'memory'
      - 'ask_user_question'
      - 'tools'
      - 'chain'
      - 'ocr'
```

Role permissions also control who can use, create, share, and publicly share skills.

## Deployment Skills

Admins can ship read-only Skills from the filesystem with `DEPLOYMENT_SKILLS_DIR`.

```bash filename=".env"
DEPLOYMENT_SKILLS_DIR=./skill
```

The directory defaults to `./skill` at the project root. LibreChat loads deployment Skills at startup and exposes them to users with the Skills capability enabled.

Deployment Skills:

- Are read-only in the UI
- Use `deployment` as their source
- Take precedence over persisted Skills with the same name
- Require a LibreChat restart after files are added, removed, or changed

Experimental [Agent Plugins](/docs/features/agent_plugins) can also bundle read-only deployment Skills together with related MCP servers. A Skill in the standalone deployment directory takes precedence over a plugin Skill with the same name.

## GitHub Skill Sync

Admins can mirror Skills from GitHub repositories with `skillSync.github` in `librechat.yaml`.

```yaml filename="librechat.yaml"
skillSync:
  github:
    enabled: true
    intervalMinutes: 60
    runOnStartup: true
    sources:
      - id: librechat-skills
        owner: your-org
        repo: your-skills-repo
        ref: main
        paths:
          - skills
        skillDiscoveryDepth: 2
        token: '${GITHUB_SKILLS_TOKEN}'
```

GitHub Skill Sync:

- Scans configured repository paths for `SKILL.md`
- Imports bundled files beside each skill
- Stores mirrored Skills with `source: "github"`
- Updates mirrored Skills when the upstream repository changes
- Removes mirrored Skills that no longer exist in the configured source
- Publishes valid Skills even when another Skill in the source is invalid or conflicts
- Reports partially successful runs and bounded skipped-Skill details to administrators
- Supports scheduled, startup, and manual admin-triggered runs

A skipped Skill keeps its last-known-good mirror when one exists. Source-wide failures such as authentication, rate limiting, lost sync ownership, or an unsuccessful rollback still fail the complete source run.

Use a GitHub fine-grained personal access token with read-only Contents and Metadata permissions for the selected repository. See [Skill Sync Object Structure](/docs/configuration/librechat_yaml/object_structure/skill_sync) for all fields, credential options, tenant scoping, and admin sync endpoints.

## Create a Skill

Open **Skills** from the side panel. You can write a skill directly in LibreChat or upload a `.md`, `.zip`, or `.skill` file that contains `SKILL.md`.

Minimum `SKILL.md`:

```md filename="SKILL.md"
---
name: brand-guidelines
description: Use when writing public-facing content that must follow the company voice and terminology.
---

# Brand Guidelines

Write in a concise, practical tone.
Prefer active voice.
Use product terminology consistently.
```

### Frontmatter

<OptionTable
  options={[
    [
      'name',
      'String',
      'Stable kebab-case identifier. It must start with a lowercase letter or digit and can contain lowercase letters, digits, and hyphens.',
      'name: brand-guidelines',
    ],
    [
      'description',
      'String',
      'The most important trigger text. Describe when the model should use the skill.',
      'description: Use when writing public-facing launch copy.',
    ],
    [
      'always-apply',
      'Boolean',
      'Automatically primes the skill into every turn where it is active.',
      'always-apply: true',
    ],
    [
      'user-invocable',
      'Boolean',
      'Set to false to hide the skill from manual `$` invocation. Default: true.',
      'user-invocable: false',
    ],
    [
      'disable-model-invocation',
      'Boolean',
      'Set to true to exclude the skill from the model-invoked skill catalog. Manual invocation is still allowed unless `user-invocable` is false.',
      'disable-model-invocation: true',
    ],
    [
      'allowed-tools',
      'Array/List of Strings',
      'Temporarily unions these tools into the agent effective tool set when the skill is manually or always applied.',
      'allowed-tools: ["execute_code"]',
    ],
    [
      'compatibility',
      'String',
      'Optional compatibility notes, such as required tools, services, or runtime assumptions. LibreChat preserves this metadata but does not enforce it.',
      'compatibility: Requires the GitHub MCP server.',
    ],
  ]}
/>

Recognized frontmatter fields are type-checked. Unrecognized fields are preserved as bounded, JSON-safe metadata instead of making the Skill invalid; deeply nested or oversized extension values are still rejected.

Keep a model-invoked Skill's `description` at 250 characters or fewer and place its most important trigger phrases first. Longer descriptions can still be imported, but LibreChat truncates them in the model-visible catalog and logs a warning naming each affected Skill.

## Invocation Modes

Skills can reach an agent in three ways:

- **Manual**: the user types `$` in chat and selects a skill from the popover.
- **Model-invoked**: the model chooses a skill from the injected skill catalog and calls the skill tool.
- **Always apply**: the skill is primed into every turn when active.

Manual invocation is explicit user intent. It can use skills that are hidden from model invocation with `disable-model-invocation: true`, as long as `user-invocable` is not false.

## Agent Scope

Agents must have **Enable skills** turned on before they can use or author Skills. The separate **Use all skills** switch controls catalog exposure.

- **All:** turn on **Use all skills** to expose the full active catalog visible to the user, including Skills added later.
- **Selected:** leave **Use all skills** off and add individual Skills to expose only those entries.
- **Authoring only:** leave **Use all skills** off with no selected Skills. Existing catalog entries are not exposed, but the agent can create a Skill during a conversation when the user has Skill create permission.
- **Disabled:** turn off **Enable skills** to disable both catalog use and runtime authoring.

This lets admins expose the Skills feature globally while keeping each agent's usable skill set focused.

Turning **Use all skills** off restores the selection that was active before the switch was enabled. Existing agents retain the legacy behavior where Skills enabled with no explicit scope means the full catalog.

## Runtime Skill Authoring

An agent with Skills enabled can create or edit reusable Skill files while it works. Runtime files use the same `skills/{skillName}/...` namespace as uploaded bundles, so the agent can author `SKILL.md` and supporting references, scripts, or assets as part of a successful workflow.

Authoring follows the normal Skills permission model:

- Creating a new Skill requires the user's Skill **Create** permission.
- Editing an existing Skill requires both edit access and inclusion in the agent's active catalog scope; deployment and GitHub-synced Skills remain read-only.
- An authoring-only agent can create a new Skill without receiving the user's existing catalog.

Runtime authoring does not bypass the deployment capability or user permissions. Removing `skills` from the Agents endpoint capabilities hides the feature, and turning **Enable skills** off on the agent disables its authoring tools.

## Active and Shared Skills

Users can toggle skills active or inactive. Owned and deployment skills default to active. Shared skills use the admin-configured default until the user overrides them.

Inactive skills are excluded from:

- The `$` popover
- The model-invoked skill catalog
- Always-apply priming

## Bundled Files

Uploaded skill bundles can include files alongside `SKILL.md`, such as:

- `references/...`
- `scripts/...`
- `assets/...`

The backend stores those files with the skill. The agent can resolve skill files when the skill is active and in scope. Nested files keep their relative paths and are addressed through the model-facing `skills/{skillName}/...` namespace. In the Skill viewer, relative Markdown links resolve from the current file and open the referenced file within the same Skill. When Code Interpreter is enabled, the same files are mounted at `/mnt/data/skills/{skillName}/...` so shell and code operations can use them directly. Editing `SKILL.md` advances the Skill version; before code or programmatic execution, LibreChat verifies every mounted file reference matches that version and re-uploads the complete bundle when any reference is stale.

<Callout type="note" title="Authoring tip">
  Keep `description` specific. It is the strongest signal for model-invoked skills. A short or vague
  description will under-trigger.
</Callout>


# Agent Plugins (https://www.librechat.ai/docs/features/agent_plugins)

Agent Plugins let an operator package related [Skills](/docs/features/skills), [MCP servers](/docs/features/mcp), and optional command hooks together on the LibreChat filesystem. LibreChat implements the Agent Plugins `1.0.0` package format and loads each immediate child directory as one plugin at startup.

<Callout type="warning" title="Experimental">
  Agent Plugins are experimental. The package format, supported components, configuration, and runtime behavior may change during the experimentation phase. Test plugins before using them in a production deployment.
</Callout>

## Configure the Directories

```bash filename=".env"
# Defaults to ./plugin relative to the LibreChat project root
DEPLOYMENT_PLUGINS_DIR=./plugin

# Persistent data allocated separately for each plugin manifest name
DEPLOYMENT_PLUGIN_DATA_DIR=./data/plugins

# Optional and disabled by default; see Command Hooks below
DEPLOYMENT_PLUGIN_HOOKS=true
```

Relative paths are resolved from the LibreChat project root. If the default `./plugin` directory does not exist, startup continues with no plugins. If `DEPLOYMENT_PLUGINS_DIR` is set explicitly but cannot be read, startup fails so a deployment mistake is not silently ignored.

Restart LibreChat after adding, removing, or changing a plugin.

## Package Layout

Each immediate child of `DEPLOYMENT_PLUGINS_DIR` is one plugin package:

```text
plugin/
  analytics-tools/
    plugin.json
    skills/
      analyze-events/
        SKILL.md
        scripts/
          analyze.py
    mcp.json
    ai.librechat/
      hooks/
        hooks.json
    scripts/
      guard.sh
```

`plugin.json` is required. `skills/`, `mcp.json`, and `ai.librechat/hooks/hooks.json` are optional and fail independently, so one invalid component does not prevent another valid component in the same plugin from loading. An invalid or missing manifest rejects the complete plugin. Diagnostics are written to the LibreChat startup logs.

## Manifest

The manifest must use the exact Agent Plugins `1.0.0` schema URL:

```json filename="plugin.json"
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "analytics-tools",
  "version": "1.0.0",
  "description": "Skills and tools for analyzing event data"
}
```

The `name` is also used for the plugin's persistent data directory. It must be 1-64 characters, start and end with a lowercase letter or number, use only lowercase letters, numbers, hyphens, and periods, and cannot contain consecutive `--` or `..` sequences. Plugin names must be unique within the deployment.

## Bundled Skills

Put each Skill in an immediate child directory under `skills/`, with `SKILL.md` at that child's root. LibreChat loads valid entries as read-only deployment Skills and applies the normal Skills capability and access rules.

The standalone [`DEPLOYMENT_SKILLS_DIR`](/docs/features/skills#deployment-skills) takes precedence over a plugin Skill with the same name. Within the plugin directory, the first package to claim a Skill name wins and later conflicts are skipped with a warning.

## Bundled MCP Servers

Define optional MCP servers in `mcp.json` with the exact `1.0.0` schema URL:

```json filename="mcp.json"
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "analytics": {
      "type": "stdio",
      "command": "node",
      "args": ["${PLUGIN_ROOT}/server.js"],
      "cwd": "${PLUGIN_ROOT}",
      "env": {
        "PLUGIN_CACHE": "${PLUGIN_DATA}/cache"
      }
    }
  }
}
```

Supported transports are `stdio`, `streamable-http`, and `sse`. Remote HTTP transports must use HTTPS except for loopback destinations. Server names must survive LibreChat's MCP tool-name normalization unchanged and cannot use reserved JavaScript object names.

For `stdio` servers, `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` are expanded once in `args`, `env`, and `cwd`; both values are also added to the launched process environment. Plugin MCP configuration does not expand arbitrary variables from LibreChat's host environment. A value such as `${OPENAI_API_KEY}` remains literal through registry storage and runtime initialization. Keep credentials outside plugin files and provide them through a purpose-built authenticated service or another deployment-controlled mechanism.

An MCP server declared in `librechat.yaml` takes precedence over a plugin server with the same name. Duplicate plugin server names are skipped and logged.

## Persistent Plugin Data

LibreChat creates one directory at `<DEPLOYMENT_PLUGIN_DATA_DIR>/<plugin-name>` and exposes it to `stdio` components as `${PLUGIN_DATA}`. Use it for plugin-owned persistent state. Do not write persistent state into `${PLUGIN_ROOT}`, which should be treated as package content.

## Command Hooks

Agent Plugins can run `command` hook handlers at Agent lifecycle boundaries. Hook execution is disabled by default. Enable it explicitly and restart LibreChat:

```bash filename=".env"
DEPLOYMENT_PLUGIN_HOOKS=true
```

When the variable is false or unset, LibreChat ignores hook documents and logs a startup warning; the plugin's independently valid Skills and MCP servers still load.

<Callout type="warning" title="Trusted code only">
  A command hook runs an operator-installed child process on the LibreChat API host. The restricted
  environment reduces accidental exposure but is not a sandbox. Enable hooks only for plugin
  packages you trust at the same level as LibreChat code and `toolApproval` hook modules.
</Callout>

Declare hooks at `ai.librechat/hooks/hooks.json`:

```json filename="ai.librechat/hooks/hooks.json"
{
  "description": "Reject writes outside the workspace",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/guard.sh",
            "timeout": 30,
            "allowedEnvVars": ["WORKSPACE_POLICY"]
          }
        ]
      }
    ]
  }
}
```

### Execution Contract

- LibreChat starts the command from the plugin root and sends a Claude-compatible event payload as JSON on standard input.
- `${PLUGIN_ROOT}`, `${PLUGIN_DATA}`, and `${CLAUDE_PLUGIN_ROOT}` are expanded once in `command` and `args` and are also available to the child process.
- The child environment contains only `PATH`, `HOME`, `LANG`, `LC_ALL`, and `TZ` when present, the three plugin path variables, and names explicitly listed in `allowedEnvVars`.
- Exit code `0` succeeds. JSON written to standard output can return event-appropriate decisions and bounded context; plain output is treated as additional context only for `SessionStart` and `UserPromptSubmit`.
- Exit code `2` blocks the applicable action or continuation and uses standard error as the reason. Other nonzero exits are logged and otherwise ignored.
- LibreChat terminates the process when the hook is aborted or times out. On POSIX hosts, commands run through Bash. Windows plugins must provide `commandWindows` or select PowerShell.

LibreChat translates the common Claude tool names `Bash`, `Write`, `Edit`, `Read`, and `WebSearch` and their file argument names to the corresponding LibreChat tools. A matcher written with LibreChat's native tool name keeps the native payload shape.

Supported source events are `RunStart`, `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PostToolBatch`, `SubagentStart`, `Stop`, `StopFailure`, `PreCompact`, and `PostCompact`. Unsupported events or declarations are skipped and reported in startup diagnostics. Only `command` handlers execute in this release; `prompt`, `http`, `mcp_tool`, and `agent` handlers remain unsupported.

`SessionStart` and handlers with `once: true` are deduplicated per conversation by a bounded process-local store. In a multi-replica deployment, the same once-only handler can run once on each process. An `ask` decision requires LibreChat's resumable human-approval flow; when that flow is unavailable, LibreChat tightens the decision to `deny` instead of leaving the run paused without a resume path.

## Related

- [Skills](/docs/features/skills)
- [Model Context Protocol](/docs/features/mcp)
- [Agents](/docs/features/agents)


# Subagents (https://www.librechat.ai/docs/features/subagents)

Subagents let a LibreChat Agent spawn an isolated child run for focused work. The child agent gets its own context window and tool execution flow. The parent receives the child result without absorbing every intermediate tool call, trace, or verbose file operation into its own context.

Use subagents for:

- Research subtasks that may generate long intermediate output
- Review passes with a specialized agent
- Tool-heavy work that should stay outside the parent context
- Multi-agent team workflows that should execute behind one delegated task
- Parallel-style decomposition where the parent coordinates and summarizes

## How Subagents Differ from Agent Chain

Agent Chain builds a multi-agent graph where agents pass results through configured chain steps. Subagents are spawned by an agent as a tool call during a run.

- **Agent Chain**: graph-level multi-agent workflow
- **Subagents**: runtime delegation from a parent agent to isolated child runs

Both can use existing agents, but subagents are designed for scoped delegation from inside a single agent's reasoning loop.

## Enable the Capability

The `subagents` capability is enabled by default. Admins can remove it from the agents endpoint capability list to disable the feature.

```yaml filename="librechat.yaml"
endpoints:
  agents:
    capabilities:
      - 'deferred_tools'
      - 'execute_code'
      - 'file_search'
      - 'web_search'
      - 'artifacts'
      - 'subagents'
      - 'actions'
      - 'context'
      - 'skills'
      - 'tools'
      - 'chain'
      - 'ocr'
```

## Configure an Agent

In the Agent Builder, open **Advanced Settings** and enable **Subagents**.

Available settings:

- **Enable subagents**: adds the subagent spawn tool to the agent.
- **Allow self-spawn**: lets the agent spawn a fresh copy of itself in an isolated context. This is enabled by default when subagents are enabled.
- **Additional subagents**: selects specific agents the parent may spawn.

Saved Agent teams are embedded as `subagents.graphs` in Agent create or update payloads. The current Agent Builder preserves these definitions while you edit the self-spawn and individual-Agent settings, but it does not author graph definitions. Each team names a bounded directed acyclic graph, its entry Agent, and the Agent whose result is returned to the parent:

```yaml filename="agent saved-team subagent"
subagents:
  enabled: true
  allowSelf: false
  graphs:
    - type: 'research_team'
      name: 'Research team'
      description: 'Researches a topic and reviews the findings'
      agent_ids:
        - 'agent_researcher'
        - 'agent_reviewer'
      entry_agent_id: 'agent_researcher'
      result_agent_id: 'agent_reviewer'
      edges:
        - from: 'agent_researcher'
          to: 'agent_reviewer'
          edgeType: 'direct'
```

Equivalent agent shape:

```yaml filename="agent subagents"
subagents:
  enabled: true
  allowSelf: true
  agent_ids:
    - 'agent_researcher'
    - 'agent_reviewer'
```

## Configure a Model Spec

Admins can also enable Subagents for ephemeral agents created from a model spec. This is useful when a model spec should behave like a focused agent profile without requiring users to create or select a persisted parent agent.

```yaml filename="modelSpecs / subagents"
modelSpecs:
  list:
    - name: 'research-assistant'
      label: 'Research Assistant'
      subagents:
        enabled: true
        allowSelf: true
        agent_ids: []
      preset:
        endpoint: 'agents'
        model: 'gpt-4o'
```

Only `enabled` and `allowSelf` are sent to clients in startup config. The `agent_ids` allowlist stays server-side and is validated against the effective Subagent limit, which defaults to 10 and is configured with [`endpoints.agents.maxSubagents`](/docs/configuration/librechat_yaml/object_structure/agents#maxsubagents). Client request payloads cannot supply or override model-spec Subagent configuration.

## Runtime Behavior

When subagents are enabled, the parent agent receives a `subagent` tool. The tool can spawn:

- `self`, when `allowSelf` is not false
- Any configured child agent in `agent_ids`
- Any complete saved Agent team in `graphs`

Child agents run with isolated context. Parent tool-search state and accumulated context are not copied into the child run. Foreground child model usage is included in the parent transaction and usage totals; detached child calls that finish after the parent request closes record usage independently. The compact child card opens a unified activity side panel for foreground, graph, legacy, and detached runs, showing bounded lifecycle, reasoning, tool, approval, and result details appropriate to that transport.

Explicit child agents are advertised to the parent as lightweight descriptors and fully initialize only when selected. Unused children do not load their model, tools, MCP servers, files, or Skills. At invocation, LibreChat rechecks view permission and the child's versioned configuration identity; if access was revoked or the saved configuration changed during the run, the child fails closed instead of executing stale settings.

When parallel branches reference the same child Agent, LibreChat evaluates cycle and depth limits independently for each traversal path. A cycle on one branch therefore does not discard valid nested Agent links on another branch.

Saved teams follow the same lazy resolution and access checks as individual children. When selected, every member retains its own model, instructions, tools, MCP credentials, Skills, files, memory, Code Interpreter profile, and billing identity. LibreChat executes the team as one isolated child graph, streams member-aware progress, and returns the configured result Agent's output. If any persisted member is missing, inaccessible, or invalid, LibreChat skips the whole team rather than running a partial graph.

Across later turns, LibreChat keeps prior calls from reachable child and team tools in structured tool-call form, including resolved MCP server identity where needed. Tools that are no longer reachable are not added to the active run merely because they appear in history.

## Detached Subagent Threads

When `run_in_background` is enabled for the Agents endpoint, a parent Agent can detach subagent work and continue without waiting for it. LibreChat persists each detached child with private execution lineage and a canonical transcript. Child threads stay out of normal conversation navigation and search and cannot be read directly; the authorized parent view exposes only bounded child activity.

Child threads are view-only execution records. A user cannot edit or delete the canonical child in place. For a completed durable direct-Agent child, **Continue in a new chat** forks the exact completed branch into a separate normal, writable Agent conversation while leaving the original child hidden and read-only. The activity panel keeps one composer mounted throughout the run and across incoming task deliveries, preserving focus and half-typed guidance while a new task view loads. Submission remains unavailable until the task can accept controls. Text entered after settlement is carried into the fork, and a failed fork restores the draft in the panel. This action is unavailable for running, graph, anonymous-Agent, and shared views. Deleting the parent cascades to its child lineage. The parent Agent can poll, steer, queue, interrupt, cancel, collect, and later continue the canonical child through its task tools; Mongo-backed leases serialize those continuations across API replicas.

The activity panel presents the selected child's turns as one branch-aware history. Use **Load earlier activity** to page backward and **Show full activity** on a turn to reveal its complete bounded run detail. Tool calls, approvals, results, controls, and user-visible reasoning can appear. Reasoning text is bounded like the rest of the projection and marked when truncated; activity persisted by older servers may show only a reasoning marker. Ordinary persisted activity uses the same per-item limits as private child activity and fits a contiguous newest suffix into a 64 KB serialized activity budget, so the shortening notice appears only when meaningful content was omitted. When history is truncated or unavailable, the panel says so instead of presenting a partial view as complete. New event turns settle at the bottom of the history, running turns show elapsed time, and actor rows use fixed-position status dots so status changes do not shift the layout. A compact floating header lets the history scroll beneath it, while the composer follows main chat's dimensions. Running work is conveyed by its streaming activity rather than a separate running status chip; abnormal terminal states remain labeled.

With Redis enabled, LibreChat records the active execution owner and routes poll, list, steer, queue, interrupt, cancel, and collect requests from any API replica to that process through bounded, expiring request/reply messages. The open activity panel also receives task-scoped live updates; a two-second durable query remains the fallback for missed events, reconnects, or Redis outages. Single-process and non-Redis deployments use the in-memory fast path. A live executor is not migrated after its owner process restarts or disappears, but the MongoDB-backed child transcript, terminal state, usage, history, and later Agent-driven continuation survive independently. This does not turn detached tasks into a public background-job API.

### Control a Running Child

In an authenticated parent conversation, open the Subagent activity panel and select a durable child task. While that task is running, type guidance and submit it to steer by default. On a hover-capable desktop, the send control's action menu offers **Steer**, **Queue**, and **Interrupt** and shows only shortcuts active under the user's current keyboard settings. On touch devices and in the modal panel, queue and interrupt remain available as icon controls. With no guidance entered, the send control becomes **Cancel task**.

- **Steer** adds guidance at the child's next safe boundary.
- **Queue** holds guidance for a later turn in the child task.
- **Interrupt** stops the child's current work at a safe boundary and applies new guidance.
- **Cancel task** requests cancellation without sending guidance.

Accepted controls appear in a task-specific history as sending, waiting, applied, not applied, or failed, with a reason when available. Choose **Withdraw** to remove queued guidance before the child applies it. Guidance is limited to 4 KB. Each task stores up to 64 authoritative receipts, while the activity panel displays up to 32; older completed history is omitted before active accepted controls, and the UI labels omitted history or guidance shortened for display.

Running controls close when the child settles, and the same composer switches to continuing an eligible child in a new chat. LibreChat verifies the signed-in user's access to the parent and child, their parent-child relationship, and tenant ownership on every request, and applies the configured content filters and moderation checks to guidance. Repeated delivery of the same accepted control returns its authoritative receipt instead of applying the instruction twice. Shared conversations remain read-only and do not expose these controls.

Shared conversations can display only the bounded activity already included in the share payload. Their view is read-only, omits approval controls and durable child selectors, and never opens the authenticated child-thread query path.

### Event-Driven Child Activity

Child Agents continued through an [Agents API event binding](/docs/features/agents_api#event-driven-child-agents) do not originate from a `subagent` tool card. In the authenticated parent conversation, LibreChat instead adds one compact actor row beneath the message that owns the binding. Opening it reuses the Subagent activity panel, with selectors for the actor and its turns plus live run-step, tool, message, and reasoning-marker activity. External event triggers render as normal user-trigger rows with expandable event type, source, occurrence time, and expected-action details. When a new delivery selects another task in the same child thread, the existing turns remain visible while the new task loads and only its streaming placeholder is appended; task-specific status and controls are never borrowed from the retained view. Earlier pages and per-turn full activity use the same bounded, read-only child history.

The parent projection is deliberately bounded: it returns at most 64 child threads, keeps up to 20 recent tasks per child from a shared bounded source window, and caps the response at 96 KB. The UI reports truncated history rather than implying that an over-limit view is complete. Starting another turn promotes an older actor into the recent-child window. These child threads remain hidden from ordinary navigation and cannot be discovered from another parent or tenant.

### Automatic Parent Continuation

After a detached child finishes, LibreChat persists its terminal result or error and, by default, sends an idempotent continuation to the saved parent Agent on the exact branch that launched it. Delivery waits while that parent branch is running, paused, or finalizing. The resumed parent receives a bounded manifest of relevant sibling tasks instead of busy-polling them, and the chat presents the wake-up in a collapsible task card.

Automatic completion delivery is enabled when [`endpoints.agents.backgroundTasks.completionWakeups`](/docs/configuration/librechat_yaml/object_structure/agents#backgroundtasks) is omitted or `true`. Set it to `false` to keep detached children poll-only. Cancelled children are ignored, and ephemeral parent Agents are not supported because there is no saved configuration to restore. Detached child execution owns its own run context, so settling or aborting the original parent request does not cancel independently owned work. The parent can still collect durable child results through its task tools.

## Limits

LibreChat enforces these limits to keep subagent graphs bounded:

<OptionTable
  options={[
    [
      'endpoints.agents.maxSubagents',
      'Number',
      'Maximum entries in each parent Agent `agent_ids` or `graphs` list. Configurable from 1 to 50.',
      '10',
    ],
    [
      'MAX_GRAPH_SUBAGENT_MEMBERS',
      'Number',
      'Maximum members in one saved Agent team.',
      '32',
    ],
    ['MAX_SUBAGENT_DEPTH', 'Number', 'Maximum explicit subagent hops from a root agent.', '5'],
    [
      'MAX_SUBAGENT_GRAPH_NODES',
      'Number',
      'Maximum unique explicit subagent targets loaded at runtime.',
      '50',
    ],
    [
      'MAX_SUBAGENT_RUN_CONFIGS',
      'Number',
      'Maximum expanded subagent configurations embedded into one run request.',
      '100',
    ],
  ]}
/>

Only the per-agent subagent count is configurable. Set `endpoints.agents.maxSubagents` in `librechat.yaml` to raise it from the default of 10, up to a hard ceiling of 50:

```yaml filename="librechat.yaml"
endpoints:
  agents:
    maxSubagents: 20
```

The configured value applies to Agent create, update, and duplicate requests for both `subagents.agent_ids` and `subagents.graphs`, to model spec `subagents.agent_ids` allowlists, and to the subagent picker in the Agent Builder. See [maxSubagents](/docs/configuration/librechat_yaml/object_structure/agents#maxsubagents) for the full reference. The per-team member, depth, graph node, and run configuration limits above are fixed.

## Access Control

Configured child agents and every saved-team member must be visible to the user. If the user lacks view access to an individual referenced Agent, LibreChat skips that subagent. A saved team is all-or-nothing and is skipped if any member is unavailable. Create and update requests reject missing or unauthorized references, duplicate spawn types, cyclic or disconnected team graphs, invalid entry or result members, and configurations that exceed the limits above.

## Design Tips

- Enable self-spawn when the parent agent is already well-scoped and just needs a fresh context for a subtask.
- Add specific child agents when the task needs a different model, instruction set, tool set, or skill allowlist.
- Use a saved team when several Agents should run as one delegated workflow with a single result returned to the parent.
- Keep child descriptions clear. The parent uses each child name and description to choose the right delegation target.
- Use subagents for intermediate work that should return a compact result, not for permanent handoffs to another conversation path.


# Agents API (Beta) (https://www.librechat.ai/docs/features/agents_api)

<Callout type="warning" title="Beta Feature">
The Agents API is currently in beta. Endpoints, request/response formats, and behavior may change as we iterate toward a stable release.
</Callout>

LibreChat exposes agents to external applications, scripts, and services through inference and event-delivery APIs.

## Overview

The Agents API provides two inference interfaces and authenticated event delivery:

- **OpenAI-compatible Chat Completions** — `POST /api/agents/v1/chat/completions`
- **Open Responses API** — `POST /api/agents/v1/responses`
- **Agent Events** — `POST /api/agents/v1/events`

The inference interfaces support API-key authentication, optional OIDC authentication, and streaming responses. Agent Events use Remote Agents API-key authentication so LibreChat can bind each delivery to a stable source identity.

LibreChat is adopting [Open Responses](https://www.openresponses.org/) as its primary API framework for serving agents. While the Chat Completions endpoint provides backward compatibility with existing OpenAI-compatible tooling, the Open Responses endpoint represents the future direction.

## Enabling the Agents API

The Agents API is gated behind the `remoteAgents` interface configuration. All permissions default to `false`.

```yaml filename="librechat.yaml"
interface:
  remoteAgents:
    use: true
    create: true
```

See [Interface Configuration — remoteAgents](/docs/configuration/librechat_yaml/object_structure/interface#remoteagents) for all available options.

**Note:** Admin users have all remote agent permissions enabled by default.

## API Key Management

Once `remoteAgents.use` and `remoteAgents.create` are enabled, users can generate API keys from the LibreChat UI. These keys authenticate requests to the Agents API.

## Authentication

The Agents API supports two authentication methods that can be used independently or together.

### API Key

API key authentication is enabled by default. Generate API keys from the LibreChat UI once `remoteAgents.use` and `remoteAgents.create` are enabled.

```bash
Authorization: Bearer <YOUR_API_KEY>
```

### OIDC Bearer Token

For machine-to-machine scenarios where your infrastructure already has an OIDC provider, you can authenticate directly with OIDC Bearer tokens without a LibreChat API key.

Configure OIDC auth in `librechat.yaml`:

```yaml filename="librechat.yaml"
endpoints:
  agents:
    remoteApi:
      auth:
        apiKey:
          enabled: false
        oidc:
          enabled: true
          issuer: https://auth.example.com/realms/myrealm
          # jwksUri is optional and auto-discovered from issuer if omitted
          audience: my-client-id
```

Then call the API with your OIDC access token:

```bash
curl -X POST https://your-librechat-instance/api/agents/v1/responses \
  -H "Authorization: Bearer YOUR_OIDC_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model": "agent_abc123", "input": "Hello!"}'
```

<Callout type="info">
  The OIDC token must belong to a user that already exists in LibreChat. Matching uses the
  `sub` claim first, then falls back to `email`, `preferred_username`, or `upn`.
</Callout>

See [Agents Endpoint - remoteApi](/docs/configuration/librechat_yaml/object_structure/agents#remoteapi) for all configuration options.

<Callout type="warning" title="Agent Events require an API key">
  OIDC-only authentication is not supported for the Agent Events endpoints. Use a Remote Agents
  API key when creating bindings, enqueueing events, and polling delivery status.
</Callout>

## Endpoints

### Chat Completions (OpenAI-compatible)

```
POST /api/agents/v1/chat/completions
```

Use any OpenAI-compatible SDK by pointing it at your LibreChat instance. The `model` parameter corresponds to an agent ID.

**Example with curl:**
```bash
curl -X POST https://your-librechat-instance/api/agents/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "agent_abc123",
    "messages": [
      {"role": "user", "content": "Hello, what can you help me with?"}
    ],
    "stream": true
  }'
```

**Example with OpenAI SDK (Python):**
```python
from openai import OpenAI

client = OpenAI(
    base_url="https://your-librechat-instance/api/agents/v1",
    api_key="YOUR_API_KEY"
)

response = client.chat.completions.create(
    model="agent_abc123",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True
)

for chunk in response:
    print(chunk.choices[0].delta.content, end="")
```

### List Models

```
GET /api/agents/v1/models
```

Returns available agents as models. Useful for discovering which agents are accessible with your API key.

### Open Responses API

```
POST /api/agents/v1/responses
```

The Open Responses endpoint follows the [Open Responses specification](https://www.openresponses.org/), an open inference standard initiated by OpenAI and built by the open-source AI community. It is designed for agentic workflows with native support for reasoning, tool use, structured outputs, and streaming semantic events.

```bash
curl -X POST https://your-librechat-instance/api/agents/v1/responses \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "agent_abc123",
    "input": "What is the weather today?"
  }'
```

## Agent Events

Agent Events let an authenticated controller or source adapter deliver durable work to an Agent. Event delivery is a beta capability and its request, response, and runtime behavior may change.

The endpoints use the authenticated user, tenant, and API key; the target Agent must be visible to that user under the existing Remote Agents permissions. Agent Event requests also use the dedicated [`rateLimits.agentEvents`](/docs/configuration/librechat_yaml/object_structure/config#ratelimits) bucket.

### Deliver an Event

```http
POST /api/agents/v1/events
Authorization: Bearer <REMOTE_AGENTS_API_KEY>
Idempotency-Key: webhook-42-resource-7
Content-Type: application/json

{
  "mode": "fire",
  "event": {
    "id": "resource-7-ready-3",
    "type": "resource.ready",
    "occurredAt": 1786967999000,
    "payload": { "resourceId": "resource-7" }
  },
  "target": { "agentId": "agent_abc123" },
  "input": "Resource resource-7 is ready. Inspect it and report the result.",
  "orderingKey": "resource-7"
}
```

Send exactly one `Idempotency-Key` header and reuse it when retrying the same source-event-to-target delivery. The key must contain 1-256 letters, numbers, or the characters `._~:/+=-`. Reusing a key with different delivery content returns a conflict.

The caller supplies the event `id`, `type`, `occurredAt`, and sanitized `payload`. LibreChat replaces any caller-supplied `event.source` with the authenticated API key identity and derives the user, tenant, request ID, and receive time. Do not place credentials or transport secrets in event payloads because accepted deliveries are persisted.

`orderingKey` is optional. Use a stable value when deliveries from different sources must remain ordered for the same resource.

A successful request returns `202 Accepted`, an opaque delivery `id`, its current status, and a `Location` header. Poll that location:

```http
GET /api/agents/v1/events/{delivery_id}
Authorization: Bearer <SAME_REMOTE_AGENTS_API_KEY>
```

The delivery can be `pending`, `leased`, `succeeded`, or `dead`. The status response includes attempts and timestamps plus a result or public error when settled; it does not expose the stored payload, ordering key, retry history, or worker identity. Status access is scoped to the same user, tenant, and API key source.

For a bound `continue`, `succeeded` means the Agent turn was admitted, not that the requested work finished. Its status therefore also exposes a durable `handling` lifecycle: `started`, followed by exactly one of `applied`, `completed_no_action`, `failed`, or `cancelled`.

Action-aware sources can include an `expectedAction` with a tool name and optional argument subset. LibreChat reports `applied` only when that exact generation finishes with host-observed tool evidence matching the contract; model-authored prose is never accepted as proof. `fire`, `steer`, and unbound `continue` deliveries reject `expectedAction`.

Successful `fire` results include the conversation and generation identity that a later `steer` event can target.

### Event-Driven Child Agents

An external system can bind a source actor to a direct child Agent, then continue that same child conversation across events. Bound child continuations are automatic after the API-key identity, binding ownership, parent relationship, and Agent authorization checks succeed; there is no separate feature toggle.

Register the actor with the same API key that will deliver its later turns:

```http
POST /api/agents/v1/events/bindings
Authorization: Bearer <REMOTE_AGENTS_API_KEY>
Idempotency-Key: championship-7-player-hanae
Content-Type: application/json

{
  "actorId": "hanae-kobayashi",
  "parentConversationId": "director-conversation-id",
  "parentMessageId": "director-message-id",
  "target": { "agentId": "agent_hanae" }
}
```

`actorId` can contain up to 128 characters. The parent must be an ordinary Agent conversation, and the target must be configured as one of that Agent's direct Subagents or an allowed self-spawn. A new binding returns `201`; an idempotent replay returns `200`. Both responses include `id`, `actorId`, `agentId`, and `threadId`.

Send later turns with the returned binding ID:

```http
POST /api/agents/v1/events
Authorization: Bearer <SAME_REMOTE_AGENTS_API_KEY>
Idempotency-Key: game-12-ply-17-hanae
Content-Type: application/json

{
  "mode": "continue",
  "bindingId": "evtbind_...",
  "event": {
    "id": "game-12-ply-17",
    "type": "chess.turn.ready",
    "occurredAt": 1786968000000,
    "payload": { "gameId": "game-12", "expectedPly": 17 }
  },
  "input": "Your clock is running. Read the position and submit one legal move.",
  "expectedAction": {
    "tool": "submit_move",
    "arguments": { "gameId": "game-12" }
  }
}
```

LibreChat resolves the child Agent, thread, latest branch leaf, and ordering lane from the binding immediately before dispatch. Caller-supplied target and ordering fields cannot redirect a bound continuation. The request's `Idempotency-Key` becomes the stable public identity for that child turn across delivery retries, generation leasing, persisted messages, live activity, and a later HITL resume.

Each binding has an automatic durable mailbox. Its next event remains queued until the current turn reaches `applied`, `completed_no_action`, `failed`, or `cancelled`; different bindings stay independent and can run in parallel. Checkpoint continuation is attempted only for a compatible initialized turn. If a checkpoint is missing or cannot be restored, LibreChat falls back to durable message history without weakening receipt, authorization, or expected-action checks.

A bound actor can pause for Ask User or tool approval. LibreChat persists the exact signed suspension before exposing the pending action, and the binding's mailbox remains blocked until that same actor invocation resumes or settles. Event Actors require the durable MongoDB checkpointer; `type: memory` is not compatible. LibreChat negotiates generation protocol v2 and selects checkpoint or history continuation automatically. Existing protocol-v1 work remains on the durable-history path until it drains. See [Generation Protocol Compatibility](/docs/configuration/redis#generation-protocol-compatibility) for mixed-version deployment requirements.

### Coalescing Observational Events

Sources that can prove several bound `continue` events are interchangeable observations can give them the same source-defined `coalesce.key`:

```json
{
  "mode": "continue",
  "bindingId": "evtbind_...",
  "event": {
    "id": "championship-7-game-12-move-18",
    "type": "chess.move.completed",
    "occurredAt": 1786968000750,
    "payload": { "gameId": "game-12", "ply": 18 }
  },
  "input": "A tournament game advanced.",
  "coalesce": { "key": "championship-commentary" }
}
```

LibreChat collects compatible events for up to 750 ms, with at most 8 events and 512 KiB of combined envelopes. The child receives one deterministic batch document, but every source event keeps its own `Idempotency-Key`, delivery record, and receipt.

Use coalescing only for non-actionable observations. It is rejected for `fire`, `steer`, unbound `continue`, and deliveries with `expectedAction`; do not use it for commands, approvals, HITL requests, fences, or events whose individual timing matters.

In the authenticated parent conversation, each bound actor appears beneath its owning parent message. The Subagent panel can switch between actors and turns, load earlier activity, and expand a turn to show its full bounded activity. Event details include the type, source, occurrence time, and expected action. Visible run steps, tools, messages, and reasoning markers are included without raw reasoning text. The parent index returns at most 64 child threads and 20 recent tasks per child from a bounded source window, reports truncation instead of implying completeness, and caps the response at 96 KB. It is parent-, user-, and tenant-authorized; bindings, source key IDs, lease tokens, and worker state are not included in the projection.

The child thread remains hidden from normal conversation navigation and read-only to human chat routes, inherits the parent's temporary or expiration policy, and is limited to one direct-child level. A binding ID alone does not grant access; continuations are scoped to the user, tenant, and API key that created it.

### Detached Event Actor Actions

Event Actor turns can detach eligible Actions and complete them durably. The delivery remains in its handling lifecycle while the Action runs; durable terminal evidence resumes the original signed actor invocation, and only the resulting host-observed tool evidence can satisfy `expectedAction`. Replays and competing replicas cannot launch the same reserved Action twice. If LibreChat cannot determine whether an external side effect began, it quarantines the launch instead of retrying it blindly.

LibreChat enables this internal completion work automatically when the selected built-in generation store advertises support. The in-memory store runs launch, completion, and continuation in one process and cannot recover that work after the process exits. Redis generation streams add durable restart recovery and replica handoff. Capability-owned delivery and recovery records remain invisible to older claimers during a mixed-version drain; there is no operator-managed producer flag.

## Token Usage Tracking

All Agents API inference requests track token usage against the user's balance when token spending is configured. Both streaming and non-streaming responses aggregate every billed primary-Agent and Subagent model call into the top-level totals. The response also includes identity-free `primary` and `subagent` token breakdowns. Cache and reasoning-token details are included where the provider reports them.

## Roadmap

- **Open Responses as primary interface** — We plan to expand the Open Responses endpoint with full support for agentic loops, tool orchestration, and streaming semantic events.
- **Anthropic Messages API** — We may add support for the Anthropic Messages API format as an additional interface in the future.

## Related Documentation

- [Agents](/docs/features/agents) — Creating and configuring agents
- [Subagents](/docs/features/subagents) — Configuring direct child Agents
- [Agents Endpoint Configuration](/docs/configuration/librechat_yaml/object_structure/agents) — Event runtime and authentication settings
- [Interface Configuration — remoteAgents](/docs/configuration/librechat_yaml/object_structure/interface#remoteagents) — Access control settings
- [Token Usage](/docs/configuration/token_usage) — Configuring token spending and balance
- [Open Responses Specification](https://www.openresponses.org/) — The open inference standard


# Artifacts - Generative UI (https://www.librechat.ai/docs/features/artifacts)

Artifacts let LibreChat Agents render generated React components, HTML, SVG, Markdown, and Mermaid content in a dedicated interactive view. Users can continue the conversation to revise the result while keeping the generated content separate from the chat response.

## Enable Artifacts for an Agent

1. Create or edit an [Agent](/docs/features/agents).
2. Open **Add tools** and select **Artifacts**.
3. Choose an artifact instruction mode.
4. Save the agent.

The available modes are:

- **Normal**: Injects LibreChat's standard instructions for React, HTML, SVG, Markdown, and Mermaid artifacts.
- **shadcn/ui**: Adds shadcn/ui component-library guidance for more polished generated interfaces.
- **Custom**: Keeps artifact rendering enabled but injects no built-in instructions. Put the complete artifact behavior you want in the agent's instructions.

Agent-level configuration is preferred because each agent can use the mode and instructions appropriate to its purpose. The administrator can hide Artifacts by removing the `artifacts` [agent capability](/docs/configuration/librechat_yaml/object_structure/agents#capabilities).

<div align="center">
  <iframe width="560" height="315" src="https://www.youtube.com/embed/GfTj7O4gmd0?si=NrtqGoodGpfANBfT" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen={true}></iframe>
</div>

## What Artifacts Support

- Interactive React components and HTML pages
- SVG images and Mermaid diagrams
- Rendered Markdown documents
- Iterative updates through follow-up messages
- Any model available to the configured agent

## Preview and Export

Use the fullscreen control in a rendered artifact preview to expand it to the complete browser display. The control follows browser Fullscreen API state, exits normally with Escape or browser controls, and is hidden when fullscreen is unavailable.

Mermaid diagrams appear as compact inline cards that can open in the artifact panel. Export a diagram as SVG or PNG from either the inline card or the panel. Mermaid previews render directly rather than loading the Sandpack bundler; PNG export applies bounded canvas dimensions to protect the browser from oversized diagrams.

## Content-Security-Policy

You may need to update your web server's Content-Security-Policy to include `frame-src 'self' https://*.codesandbox.io` in order to load generated HTML apps in the Artifacts panel. This is a dependency of the [sandpack](https://sandpack.codesandbox.io/) library.

## Self-Hosting the Sandpack Bundler

Artifacts in LibreChat use CodeSandbox's Sandpack library to securely render HTML/JS code. By default, LibreChat connects to CodeSandbox's public CDN, which may also transmit telemetry for its usage.

For enhanced privacy, security compliance, or isolated network environments, you can [self-host the bundler.](https://sandpack.codesandbox.io/docs/guides/hosting-the-bundler)

### Why Self-Host the Sandpack Bundler?

[Self-hosting the bundler](https://sandpack.codesandbox.io/docs/guides/hosting-the-bundler) provides several advantages:

- **Privacy & Security**: Keep code execution within your own infrastructure
- **Reliability**: Remove dependency on external services
- **Performance**: Reduce latency by hosting the bundler in your network
- **Compliance**: Meet organizational data handling requirements
- **Control**: Configure and customize to your specific needs

### Configuration

Once you have a self-hosted bundler set up, simply configure LibreChat to use it by setting the environment variable:

```bash
# `.env` file
SANDPACK_BUNDLER_URL=http://your-bundler-url
```

### General Considerations

Self-hosting the bundler introduces some additional overhead:

- **CORS Configuration**: You'll need to manage cross-origin resource sharing policies
- **Security Management**: You become responsible for the security of the bundler service
- **Maintenance**: Updates and patches will need to be applied manually
- **Resource Requirements**: Additional server resources will be needed to host the bundler

For detailed instructions on setting up and maintaining your self-hosted bundler, refer to the [forked CodeSandbox repository](https://github.com/LibreChat-AI/codesandbox-client) tailored for LibreChat deployment, including the removal of telemetry from Sandpack.


# Code Interpreter API (https://www.librechat.ai/docs/features/code_interpreter)

## Introduction

LibreChat's Code Interpreter API provides a secure and hassle-free way to execute code and manage files through a simple API interface. Whether you're using it through LibreChat's Agents or integrating it directly into your applications, the API offers a powerful sandbox environment for running code in multiple programming languages.

<Video
  src="https://firebasestorage.googleapis.com/v0/b/superb-reporter-407417.appspot.com/o/Sequence%2001.mp4?alt=media&token=595b0716-12f1-4516-be93-c7004a85b540"
  title="Code Interpreter demo"
  aspectRatio="16/9"
/>

<Callout type="info" title="Open source">
  LibreChat's Code Interpreter is powered by [**ClickHouse/code-interpreter**](https://github.com/ClickHouse/code-interpreter), an open-source (Apache 2.0) sandboxed code-execution service. Self-host it and point LibreChat at your own instance.
</Callout>

## Getting Started

1. Deploy the [code-interpreter](https://github.com/ClickHouse/code-interpreter) service (Docker Compose or Helm, see the repository's README)
2. Point LibreChat at it with `LIBRECHAT_CODE_BASEURL`
3. Configure [LibreChat JWT authentication](#self-hosted-jwt-authentication) for the current open-source service, or set `LIBRECHAT_CODE_API_KEY` for an API-key-compatible deployment
4. To experiment with stateful Agent sessions, deploy a separate `stateful`-profile Code Interpreter route and configure `LIBRECHAT_CODE_BASEURL_STATEFUL` or named [`statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions)
5. Start executing code and generating files securely through LibreChat's Agents or the "Run Code" button

## Key Features

### Supported Languages

Execute code in multiple programming languages:

- Python, Node.js (JS/TS), Go, C/C++, Java, PHP, Rust, Fortran, Rscript

### Seamless File Handling

- Upload files for processing
- Download generated outputs
- Secure file management
- Session-based file organization

LibreChat restores referenced conversation files into the current Code Interpreter environment before an Agent run. If every required Code Interpreter file fails recovery, the run stops before model invocation and asks the user to reattach the files rather than continuing with stale references.

When conversation inputs would mount at the same sandbox destination, LibreChat resolves the collision before execution. Shared conversation files are assigned before Agent-private files; within each scope, the newest content write keeps the original destination. Conflicting paths are flattened when needed or receive an identity-derived suffix, and the resolved `/mnt/data/...` paths are included in the Agent's file context. This also covers an upload and a generated output that share a filename, so one collision does not reject every later Code Interpreter run in the conversation. Duplicate seed references that still resolve to an already-claimed destination are skipped instead of failing the complete request.

Sandbox images are transferred in bounded windows. Set [`LIBRECHAT_CODE_SANDBOX_OUTPUT_MAX_SIZE`](/docs/configuration/dotenv#code-interpreter) to match the worker's `SANDBOX_OUTPUT_MAX_SIZE`; LibreChat derives the largest safe image window from that budget and can learn a smaller per-service limit after one failed read. Use `LIBRECHAT_CODE_IMAGE_CHUNK_BYTES` only when an exact window override is required. Previously downloaded generated artifacts are reused within the request when possible, avoiding another sandbox read.

In a multi-Agent graph, give files private to different Agents distinct filenames. Each Agent resolves its private file paths before the graph combines their inputs, so genuinely different private files that independently claim the same destination cannot yet be renamed consistently at merge time; the first seeded destination wins.

### Security & Convenience

- Secure sandboxed execution environment
- Strong isolation modes (NsJail or microVM via libkrun)
- No local setup required for end users
- Session-based, isolated file storage

### Programmatic Tool Calling

Programmatic Tool Calling lets LibreChat Agents route selected MCP tools through the Code Interpreter sandbox. Instead of asking the model to call each tool directly, LibreChat provides a Code Interpreter-backed orchestration tool; generated sandbox code can call registered tool stubs, use loops and conditionals, process intermediate results, and return a final answer.

The sandbox still does not receive general network access. Tool calls are brokered through the Code Interpreter Tool Call Server. LibreChat intersects the live caller-capability projection with its trusted registry, so only active programmatic tools already registered for the Agent can be called and a request cannot expand its own access.

Programmatic tools require Code Interpreter on the Agent. Their toggles stay disabled until Code Interpreter is selected, and removing Code Interpreter clears existing Programmatic selections. LibreChat also strips stale programmatic caller options from Agent create, update, duplicate, and version-restore operations when `execute_code` or the Code Interpreter tool is unavailable.

During a programmatic execution, expand the Code Interpreter card to see a live terminal-style trace of inner tool calls with bounded argument previews, statuses, durations, and failures. This trace helps inspect the program while it runs, but it is not persisted across a page reload.

### Background Code Execution

Background execution lets an Agent dispatch long-running code or shell work and continue the conversation. For saved Agents, supported content-only completions are delivered automatically in a follow-up continuation by default. `check_background_task` remains available for explicit status, control, live artifacts, and recovery. In LibreChat chat, when the work finishes, stdout and generated files appear on the original code call and are persisted for subsequent turns.

This feature is opt-in at the deployment level. Add `run_in_background` alongside `execute_code` in the [Agents endpoint capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities), then enable Code Interpreter on the agent. Code execution and shell calls become background-eligible automatically; turn off **Background execution** in the Code Interpreter tool settings when an agent should opt out. The model still decides per call whether to dispatch eligible work in the background.

Ordinary background execution remains process-local and does not survive the loss of its app worker. Once a content-only terminal result is persisted, its automatic delivery is durable and may continue on another replica; tasks with a live artifact still require polling on the owning run. Set [`endpoints.agents.backgroundTasks.completionWakeups: false`](/docs/configuration/librechat_yaml/object_structure/agents#backgroundtasks) to require polling for every result. Background dispatch does not extend the Code Interpreter service's execution limit; the deployment's normal server-side timeout still applies.

### Stateful Code Sessions

Stateful code sessions let a LibreChat Agent reuse one Code Interpreter sandbox workspace across executions. Files, installed packages, and working state usually carry over, making iterative analysis and multi-step file generation more efficient. Each agent selects one workspace scope:

- **User workspace (recommended):** one workspace for the signed-in user across stateful-enabled agents
- **Agent + user workspace:** one workspace for each user and agent combination
- **Conversation workspace:** one workspace for each user and conversation

For newly created Agents, the initial scope comes from **Settings > Data Controls > Code execution > Default stateful workspace**. The personal setting defaults to **User workspace**; changing it does not modify existing Agents or enable stateful sessions. Deployment administrators can restrict selectable scopes with [`statefulCodeSessions.allowedEnvironments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions). If a saved personal default becomes unavailable, new Agents use the first allowed scope; an existing enabled Agent with a now-disallowed scope must be reconfigured before it can run.

The workspace scope is separate from its execution backend. When administrators configure named [`statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions), the Agent Builder adds an **Execution environment** selector:

- **Managed:** a stateful Code API operated by the deployment
- **Attached:** a compatible Code API remote bridge that leases work to an outbound `@librechat/code` worker, such as an operator-managed VM
- **Personal:** an owner-bound `@librechat/code` worker paired by an authorized user through LibreChat settings
- **Deployment default:** the one configured environment marked as the default

<Callout type="warning" title="Highly experimental">
  Stateful Code Sessions are in an early experimentation phase. Their behavior, configuration, persistence characteristics, and underlying integration may change substantially. Do not treat the current implementation as a stable production contract.
</Callout>

This feature is opt-in. Enable `execute_code` and `stateful_code_sessions` in the [Agents endpoint capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities), configure a dedicated stateful route with [`LIBRECHAT_CODE_BASEURL_STATEFUL`](/docs/configuration/dotenv#stateful-code-interpreter-endpoint) or named environments, enable Code Interpreter on the agent, then turn on **Stateful code sessions** and choose its execution backend and workspace scope under Advanced settings.

Stateful requests fail closed when the selected environment is missing, inaccessible, or incompatible; LibreChat does not send them to `LIBRECHAT_CODE_BASEURL` or silently choose another named backend. When no named environments are configured, `LIBRECHAT_CODE_BASEURL_STATEFUL` remains the stateful route. Stateless agents continue using the normal endpoint. LibreChat also sends `X-CodeAPI-Expected-Profile: stateful`, so the dedicated service must advertise the `stateful` profile.

Stateful and stateless sessions do not share a live workspace. The stateful workspace may reset at any time, regardless of its selected scope. Save important outputs under `/mnt/data`, and do not otherwise rely on session state as durable storage.

Files authored by stateful `create_file` and `edit_file` calls appear on the assistant message under an expandable **Workspace changes** row. It lists each unique changed path for that response and provides an authenticated download action. Downloads use LibreChat's persisted file flow when available and a secure Code Interpreter fallback otherwise. The row is a record of authored outputs, not a durable workspace snapshot; stateless outputs continue to appear as regular inline attachments.

#### Attached environments and pairing

Attached environments require a matching experimental Code Interpreter remote-bridge build. The bridge remains LibreChat's authenticated policy, queue, and result boundary, while the `@librechat/code` worker connects outbound from the attached machine; the machine does not need an inbound public worker port. LibreChat does not prewarm attached environments.

An operator can pin a deployment-owned attached environment to one worker with `workerId`, and can add `pairing.workerId` when LibreChat should issue an administrator pairing code for that same route. `pairing.tokenEnv` names the environment variable containing the bridge administrator token; the secret itself never belongs in YAML. Paired control planes require HTTPS outside loopback development.

For self-service workers, configure a deployment-owned attached control plane with `pairing.allowPrincipalWorkers: true` and `pairing.tokenEnv`. It may omit both worker IDs and exist only as an enrollment control plane; such an entry is not selectable for execution and cannot be the deployment default.

Users with Code Environment management permission can then open **Settings > Code environments**, name an environment, choose an approved control plane, and select **Connect VM**. LibreChat shows a short-lived, one-time `librechat-code pair ...` command. Run it on the user's VM, then start `@librechat/code` with `LIBRECHAT_CODE_SANDBOX_ENDPOINT` pointing to that machine's sandbox service. The UI lists the user's environments and can revoke and remove them later; sandbox files remain on the VM.

Personal environments are bound to the authenticated user and tenant, protected by Code Environment ACLs, and assigned only server-validated IDs and approved base URLs. LibreChat does not expose the bridge administrator token or arbitrary operator URLs to the client. Removing an environment revokes its worker credential before deleting the registry entry, and user deletion schedules the same revocation and cleanup with background reconciliation for interrupted attempts. The selected worker route applies consistently to Code Interpreter, shell, generated-file access, and Programmatic Tool Calling.

#### Attached-environment tool permissions

Attached and personal environments use an ask-by-default approval policy for file writes and command or code execution. Read-only and search operations continue under the deployment's regular tool policy. If the administrator exposes either category through [`configSchema.permissions`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions), users with Code Environment management permission can choose among the allowed **Allow**, **Ask**, or **Deny** values under **Settings > Code environments**. Missing, stale, or disallowed settings fall back safely to **Ask**.

These controls cover file-authoring tools, shell and code execution, compile checks, and Programmatic Tool Calling. Writes to persistent Skill files still require confirmation when Skill authoring is available, even when ordinary file writes are allowed. The settings do not change VM isolation, networking, mounts, privileged execution, ingress, egress, or secret access.

The deployment's explicit [`toolApproval.enabled: false`](/docs/configuration/librechat_yaml/object_structure/agents#toolapproval) setting is an emergency override that disables this baseline. Without that override, a caller that cannot present and resume approval requests fails closed instead of silently executing an attached-environment tool.

Pairing authenticates the remote worker transport; it is not a sandbox boundary. Keep the worker's local execution endpoint on loopback or a private network and use an appropriate isolation mode for untrusted code.

While an execution is active, expanded code and shell detail panes follow streamed arguments to the bottom. Scrolling upward pauses the follow behavior so you can inspect earlier output without the pane pulling the viewport away.

## Using the API

### In LibreChat

The API has first-class support in LibreChat through these main methods:

1. **[AI Agents](/docs/features/agents#code-interpreter)**: Enable Code Interpreter in your agent's configuration to allow it to execute code and process files automatically.

2. **Manual Execution**: Use the "Run Code" button in code blocks within the chat interface, as shown here:

3. **[Programmatic Tool Calling](/docs/features/agents#programmatic-tool-calling)**: Enable the `programmatic_tools` capability and mark selected MCP tools as Programmatic so agents can orchestrate those tools from sandboxed code.

4. **[Background Tool Calls](/docs/features/agents#background-tool-calls)**: Let an agent dispatch eligible code in the background and continue while it runs.

5. **[Stateful Code Sessions](/docs/features/agents#stateful-code-sessions)**: Experimentally reuse a user-, agent-and-user-, or conversation-scoped workspace across an agent's code executions.

![Code Interpreter in LibreChat](/images/agents/run_code.png)

### Set up API key

- **Per-user setup:** Input your API key in LibreChat when prompted.
- **Global setup:** Set `LIBRECHAT_CODE_API_KEY` in LibreChat's `.env` to provide one key for all users.

The current [ClickHouse/code-interpreter](https://github.com/ClickHouse/code-interpreter) service uses LibreChat JWT authentication outside local mode. API keys remain available for compatible Code Interpreter deployments.

### Self-hosted JWT authentication

With JWT authentication, LibreChat signs a short-lived bearer token for the authenticated user on each Code Interpreter request. The Code Interpreter service verifies the matching public key and uses the signed user and tenant context to isolate files and sessions. The private signing key stays in LibreChat; Code Interpreter receives only its public verifier.

Configure LibreChat with:

```env
CODEAPI_AUTH_PROVIDER=librechat-jwt
CODEAPI_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
CODEAPI_JWT_ALGORITHM=EdDSA
CODEAPI_JWT_KID=lc-codeapi-2026-05
CODEAPI_JWT_ISSUER=librechat
CODEAPI_JWT_AUDIENCE=codeapi
CODEAPI_JWT_TTL_SECONDS=300
CODEAPI_JWT_MINT_CACHE_SECONDS=30
CODEAPI_JWT_SINGLE_TENANT_ID=legacy
```

`CODEAPI_JWT_PRIVATE_KEY_BASE64` can hold a base64-encoded PEM instead, and `CODEAPI_JWT_PRIVATE_JWK_JSON` accepts a private JWK. LibreChat supports Ed25519 (`EdDSA`, the default) and RSA (`RS256`) signing. See the [environment-variable reference](/docs/configuration/dotenv#code-interpreter-jwt-authentication) for every LibreChat-side setting.

Configure Code Interpreter with the corresponding verifier:

```env
LOCAL_MODE=false
CODEAPI_AUTH_PROVIDER=librechat-jwt
CODEAPI_JWT_ISSUER=librechat
CODEAPI_JWT_AUDIENCE=codeapi
CODEAPI_JWT_ALLOWED_ALGS=EdDSA
CODEAPI_JWT_JWKS_JSON={"keys":[{"kty":"OKP","crv":"Ed25519","x":"...","kid":"lc-codeapi-2026-05","alg":"EdDSA"}]}
CODEAPI_JWT_SINGLE_TENANT_ID=legacy
```

The issuer, audience, key ID, algorithm, and public key must match LibreChat's signer. Code Interpreter can also load verifier keys from `CODEAPI_JWT_PUBLIC_KEY` plus `CODEAPI_JWT_KID`, or from `CODEAPI_JWT_PUBLIC_KEYS_DIR`; an inline JWKS is convenient for rotation. Keep the token TTL at or below the Code Interpreter service's `CODEAPI_JWT_MAX_TTL_SECONDS`, which defaults to and is capped at 300 seconds.

For a local checkout, the Code Interpreter repository includes a helper that generates matching Ed25519 signing and execution-manifest keys and updates both `.env` files:

```bash
node scripts/setup-local-auth-env.js --librechat /path/to/LibreChat
```

Single-tenant deployments can leave `CODEAPI_JWT_SINGLE_TENANT_ID=legacy`, but the value must match on both services. Multi-tenant deployments should provide an authenticated tenant context and enable `TENANT_ISOLATION_STRICT=true` in LibreChat plus `CODEAPI_TENANT_ISOLATION_STRICT=true` in Code Interpreter. Strict mode rejects requests without tenant context instead of using the single-tenant fallback.

### Direct API Integration

The current self-hosted service expects a bearer token that satisfies its LibreChat JWT claim contract. A direct integration must mint compatible short-lived tokens and send them in the `Authorization: Bearer <token>` header. API-key-compatible deployments instead accept their configured key in the `x-api-key` header. Never use Code Interpreter's authentication-free local mode on a public or production endpoint.

### Self-hosted base URL

Set `LIBRECHAT_CODE_BASEURL` to point LibreChat at your self-hosted [code-interpreter](https://github.com/ClickHouse/code-interpreter) instance, then configure [JWT authentication](#self-hosted-jwt-authentication). For an API-key-compatible service, set `LIBRECHAT_CODE_API_KEY` instead. Highly experimental stateful Agent sessions require a separate `stateful`-profile route configured through [`LIBRECHAT_CODE_BASEURL_STATEFUL`](/docs/configuration/dotenv#stateful-code-interpreter-endpoint) or named [`statefulCodeSessions.environments`](/docs/configuration/librechat_yaml/object_structure/agents#statefulcodesessions).

## Core Functionality

### Code Execution

- Run code snippets in supported languages
- Receive stdout/stderr output
- Get execution statistics (memory usage, CPU time)
- Handle program arguments
- Access execution status and results

### File Operations

- Upload input files
- Download generated outputs
- Preview generated Office files, including PowerPoint `.pptx` presentations and `.potx` templates, plus CSV, text, and PDF-like artifacts inline when LibreChat can safely extract/render them
- Display PNG, JPEG, GIF, and WebP images returned by an Agent's `read_file` tool as viewable artifacts
- List available files
- Delete unnecessary files
- Manage file sessions

Generated artifact previews are intentionally size-bounded. Set [`FILE_PREVIEW_MAX_EXTRACT_BYTES`](/docs/configuration/dotenv#inline-file-previews) in LibreChat's `.env` to change the source-file size limit for inline preview extraction; larger files remain available for download.

Sandbox images returned through `read_file` have a 1 MiB inline limit. Larger images remain in the sandbox for processing with `bash_tool`. `LIBRECHAT_CODE_IMAGE_CHUNK_BYTES` controls only the transport chunk size used while reading eligible images; it does not raise the inline limit.

<Callout type="warning" title="Upgrade check for legacy code outputs">
  Deployments that regenerated the same Code Interpreter filename before the unique output-file index was added may have duplicate MongoDB records that prevent the index from building. Preview the repair with `npm run migrate:code-file-duplicates:dry-run`, then apply it with `npm run migrate:code-file-duplicates`. The migration keeps the newest canonical filename, renames older records with numeric suffixes, and builds the unique index after a successful run.
</Callout>

### Limitations
- Code cannot access the network
- Only 10 files can be generated per run
- Resource limits (RAM per execution, file upload size, and request quotas) depend on how you provision and configure your deployment

## Use Cases

- **Code Testing**: Test code snippets in multiple languages
- **File Processing**: Transform and analyze files programmatically
- **AI Applications**: Execute AI-generated code securely
- **Development Tools**: Build interactive coding environments
- **Objective Logic**: Verify code logic and correctness, improving AI models

## Open Source & Self-Hosting

The Code Interpreter service is open source under the Apache 2.0 license at [ClickHouse/code-interpreter](https://github.com/ClickHouse/code-interpreter). Keeping code execution as a separate service keeps the core LibreChat application lightweight: you only deploy the sandbox infrastructure when you need it, and you can scale it independently of the chat app.

The service runs as a set of independently scalable components (an API gateway, sandboxed workers, and a file server) and supports strong isolation modes (NsJail or a microVM via libkrun) so you can run untrusted code safely. See the repository's README for Docker Compose and Helm deployment instructions.

---

## Conclusion

The Code Interpreter API provides a secure, convenient way to execute code and manage files in an isolated sandbox. Whether you're using it through LibreChat's Agents or integrating it directly into your applications, it offers a robust solution for code execution needs.

For detailed technical specifications, deployment guides, and the API reference, see the [code-interpreter repository](https://github.com/ClickHouse/code-interpreter).


# Web Search (https://www.librechat.ai/docs/features/web_search)

LibreChat's web search feature allows you to search the internet and retrieve relevant information to enhance your conversations. The feature consists of three main components that work together to provide comprehensive search results.

## Quick Start

To get started with web search, configure a search provider and scraper. Most providers require API keys; Keenable works without one for both search and page fetch, with an optional key to raise its public rate limits. Reranking can use Jina or Cohere, or be disabled with `rerankerType: "none"`. You can configure the stack in two ways:

<Callout type="warning" title="Private self-hosted endpoints">
  Web search, scrape, and rerank connections block private, loopback, link-local, and cloud-metadata destinations by default. If SearXNG, Firecrawl, Jina, or another configured provider endpoint is private, add its exact host and port to [`webSearch.allowedAddresses`](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers).
</Callout>

1. **Environment Variables** (Recommended for admins):
   ```bash
   # Search Provider (choose one)
   SERPER_API_KEY=your_serper_api_key
   # or
   SEARXNG_INSTANCE_URL=your_searxng_instance_url
   SEARXNG_API_KEY=your_searxng_api_key  # Optional
   # or
   TAVILY_API_KEY=your_tavily_api_key
   # or use Keenable keyless; this optional key raises public rate limits
   KEENABLE_API_KEY=your_keenable_api_key

   # Scraper (choose one)
   FIRECRAWL_API_KEY=your_firecrawl_api_key
   # Optional: Custom Firecrawl API URL
   FIRECRAWL_API_URL=your_firecrawl_api_url
   # Optional: Firecrawl API version (v0 or v1)
   # FIRECRAWL_VERSION=v1
   # or
   TAVILY_API_KEY=your_tavily_api_key
   # or use Keenable keyless with the same optional key

   # Reranker (Optional - choose one, or set rerankerType: "none")
   JINA_API_KEY=your_jina_api_key
   # Optional: Custom Jina API URL
   JINA_API_URL=your_jina_api_url
   # or
   COHERE_API_KEY=your_cohere_api_key
   ```

   Using Keenable without an environment key still requires provider selection in `librechat.yaml`:

   ```yaml filename="librechat.yaml"
   webSearch:
     searchProvider: keenable
     scraperProvider: keenable
     rerankerType: none
   ```

   See the [full Keenable configuration](/docs/configuration/librechat_yaml/object_structure/web_search#keenablescraperoptions) for optional search, fetch, and rate-limit settings.

2. **User Interface** (If environment variables are not set):
   - Users will be prompted to enter the required API keys when they first use the web search feature
   - They can choose which search provider (Serper, SearXNG, Tavily, or Keenable), scraper, and reranker service (Jina, Cohere, or none) to use

## Obtaining API Keys

Each enabled external service requires its own API key. Here's how to obtain them:

### Search Providers

#### Serper
1. Visit [Serper.dev](https://serper.dev)
2. Sign up for an account
3. Navigate to the API Key section
4. Copy your API key
5. Set it in your environment variables or provide it through the UI

#### SearXNG
1. Follow the setup instructions in the [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search#setting-up-searxng) documentation
2. Set `SEARXNG_INSTANCE_URL` to your instance URL
3. Optionally set `SEARXNG_API_KEY` if your instance requires authentication
4. Add your instance's exact `host:port` to [`allowedAddresses`](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers) if it is on a private or loopback address, otherwise LibreChat blocks the connection
5. Optionally tune which engines your instance queries with [`searxngSearchOptions`](/docs/configuration/librechat_yaml/object_structure/web_search#searxngsearchoptions). The default set is only three engines and includes DuckDuckGo, which serves CAPTCHAs to most self-hosted instances, so widening it helps if searches come back empty

#### Tavily
1. Visit [Tavily](https://app.tavily.com/home)
2. Sign up for an account
3. Copy your API key
4. Set `TAVILY_API_KEY` in your environment variables or provide it through the UI
5. Tavily can be used as both a search provider and a scraper provider

#### Keenable

1. Select Keenable as the search provider, scraper, or both
2. No API key is required for the public search and fetch endpoints
3. Optionally set `KEENABLE_API_KEY` to raise the public rate limits
4. Use `KEENABLE_API_URL` only to override the search endpoint and `KEENABLE_FETCH_URL` only to override the page-fetch endpoint

### Scraper: Firecrawl

1. Visit [Firecrawl.dev](https://docs.firecrawl.dev/introduction#api-key)
2. Sign up for an account
3. Navigate to the API Key section
4. Copy your API key
5. Set it in your environment variables or provide it through the UI
6. (Optional) If you're using a custom Firecrawl instance, you'll also need to set the API URL

### Rerankers

#### Jina
1. Visit [Jina.ai](https://jina.ai/api-dashboard/)
2. Sign up for an account
3. Navigate to the API Dashboard
4. Copy your API key
5. Set it in your environment variables or provide it through the UI

#### Cohere
1. Visit [Cohere Dashboard](https://dashboard.cohere.com/welcome/login)
2. Sign up for an account
3. Navigate to the API Keys section
4. Copy your API key
5. Set it in your environment variables or provide it through the UI

## Components

### 1. Search Providers

Search providers are responsible for performing the initial web search and returning relevant results.

**Available Providers:**
- **Serper**: A Google Search API that provides high-quality search results
  - Get your API key from [Serper.dev](https://serper.dev/api-keys)
- **SearXNG**: Open-source, self-hosted meta search engine
  - Self-host your own instance
  - Privacy-focused search results
  - Configurable engines, result language, time range, and request timeout
- **Tavily**: AI-optimized search API
  - Get your API key from [Tavily](https://app.tavily.com/home)
  - Supports configurable search depth, topic filtering, domain filtering, and more
  - Can also serve as a scraper provider
- **Keenable**: Keyless web search with optional higher-rate authentication
  - Supports domain-restricted search, result limits, attribution titles, and request timeouts
  - Can also serve as a scraper provider

### 2. Scrapers

Scrapers extract the actual content from web pages returned by the search provider.

**Available Scrapers:**
- **Firecrawl**: A powerful web scraping service that extracts content from web pages
  - Get your API key from [Firecrawl.dev](https://docs.firecrawl.dev/introduction#api-key)
  - API URL is optional (defaults to Firecrawl's hosted service)

- **Tavily**: Batch URL extraction via Tavily Extract API
  - Uses the same `TAVILY_API_KEY` as the search provider
  - Supports configurable extract depth, image extraction, and favicon extraction

- **Keenable**: Keyless page fetch
  - Uses the same optional API key as Keenable search
  - Supports attribution titles and request timeouts

**Planned Scrapers:**
- **Local Firecrawl**: Self-hosted version of Firecrawl
- Additional third-party scraping services

### 3. Rerankers

Rerankers analyze the scraped content to determine the most relevant parts and reorder them for better results.

**Available Rerankers:**
- **Jina**: AI-powered reranking service
  - Get your API key from [Jina.ai](https://jina.ai/api-dashboard/)
  - API URL is optional (defaults to Jina's hosted service)
- **Cohere**: Advanced reranking service
  - Get your API key from [Cohere Dashboard](https://dashboard.cohere.com/welcome/login)
- **None**: Skips reranking when `rerankerType` is set to `"none"`

**Planned Rerankers:**
- **RAG API**: Open-source reranking using RAG (Retrieval-Augmented Generation)
- Additional third-party reranking services

## Configuration

### Admin Configuration

Admins can configure the web search feature using environment variables. The YAML configuration allows you to specify custom environment variable names for each component.

⚠️ **Important: Never put actual API keys or values in the YAML file (they won't work)- only use environment variable names.**

```yaml
webSearch:
  # Search Provider Configuration
  serperApiKey: "${CUSTOM_SERPER_API_KEY}"  # ✅ Correct: Using environment variable name
  # serperApiKey: "sk-123..."               # ❌ Wrong: Never put actual API keys here
  # or
  searxngInstanceUrl: "${CUSTOM_SEARXNG_INSTANCE_URL}"  # ✅ Correct: Using environment variable name
  searxngApiKey: "${CUSTOM_SEARXNG_API_KEY}"            # ✅ Correct: Using environment variable name
  # searxngInstanceUrl: "http://..."        # ❌ Wrong: Never put actual URLs here
  # searxngApiKey: "sk-123..."              # ❌ Wrong: Never put actual API keys here
  searxngSearchOptions:                     # Query options, not secrets, so real values belong here
    engines: "google,bing,startpage"
    language: "en"

  # Tavily Configuration (search and/or scraper)
  tavilyApiKey: "${CUSTOM_TAVILY_API_KEY}"
  tavilySearchUrl: "${CUSTOM_TAVILY_SEARCH_URL}"
  tavilyExtractUrl: "${CUSTOM_TAVILY_EXTRACT_URL}"

  # Keenable Configuration (search and/or scraper; keyless by default)
  keenableApiKey: "${CUSTOM_KEENABLE_API_KEY}" # Optional; raises rate limits
  keenableApiUrl: "${CUSTOM_KEENABLE_API_URL}" # Optional search URL override
  keenableSearchOptions:
    maxResults: 8
    site: "example.com"
    attributionTitle: "LibreChat"
    timeout: 15000
  keenableScraperOptions:
    attributionTitle: "LibreChat"
    timeout: 15000

  # Scraper Configuration
  firecrawlApiKey: "${CUSTOM_FIRECRAWL_API_KEY}"
  firecrawlApiUrl: "${CUSTOM_FIRECRAWL_API_URL}"
  # firecrawlApiKey: "fc-123..."            # ❌ Wrong: Never put actual API keys here
  # firecrawlApiUrl: "https://..."          # ❌ Wrong: Never put actual URLs here

  # Reranker Configuration
  jinaApiKey: "${CUSTOM_JINA_API_KEY}"
  jinaApiUrl: "${CUSTOM_JINA_API_URL}"
  cohereApiKey: "${CUSTOM_COHERE_API_KEY}"
  # jinaApiKey: "jn-123..."                 # ❌ Wrong: Never put actual API keys here
  # jinaApiUrl: "https://..."               # ❌ Wrong: Never put actual URLs here
  # cohereApiKey: "ch-123..."               # ❌ Wrong: Never put actual API keys here

  # General Settings
  safeSearch: 1 # Options: 0 (OFF), 1 (MODERATE - default), 2 (STRICT)
```

**Note:** The YAML configuration should only contain environment variable names (in the format `${VARIABLE_NAME}`). This flexibility enables:
- Using different variable names in different environments
- Supporting multiple configurations for different user groups
- Future integration with role-based configurations

If you want to restrict the system to use only specific services, you can specify the service types:

```yaml
webSearch:
  # ... variable configurations ...
  searchProvider: "serper"    # Only use Serper for search
  # searchProvider: "searxng" # Only use SearXNG for search
  # searchProvider: "tavily"  # Only use Tavily for search
  # searchProvider: "keenable" # Use keyless Keenable search
  scraperProvider: "firecrawl"    # Only use Firecrawl for scraping
  # scraperProvider: "tavily" # Only use Tavily for scraping
  # scraperProvider: "keenable" # Use keyless Keenable page fetch
  rerankerType: "jina"        # Options: "jina", "cohere", "none"
```

### User Configuration

If the admin hasn't configured all the necessary API keys, users will be prompted to provide them through the UI. The interface allows users to:

1. Choose their preferred reranker (Jina, Cohere, or none)
2. Enter API keys for the required services
3. Configure the Firecrawl API URL if needed (optional)
4. Configure the Jina API URL if needed (optional)
5. Configure Tavily or Keenable endpoint overrides if needed (optional)

## Usage

Once configured, you can use web search in two ways:

1. **Chat Interface**: Click the web search button in the chat interface to enable web search for your conversation
2. **Agents**: Use the `web_search` capability in agents to allow them to search the web

## Notes

- Search provider and scraper configuration are required; reranking can be disabled with `rerankerType: "none"`
- The Firecrawl API URL is optional and defaults to their hosted service
- The Jina API URL is optional and defaults to their hosted service
- The Tavily Search and Extract API URLs are optional and default to Tavily's hosted services
- Keenable search and page fetch are keyless by default; an optional key raises public rate limits
- `KEENABLE_API_URL` overrides only search, while `KEENABLE_FETCH_URL` overrides only page fetch
- Pair `searchProvider: "keenable"`, `scraperProvider: "keenable"`, and `rerankerType: "none"` for a fully keyless stack
- Safe search provides three levels of content filtering: OFF (0), MODERATE (1 - default), and STRICT (2)
- Tavily does not inherit the global `safeSearch` setting by default; use `tavilySearchOptions.safeSearch` only if your Tavily account supports `safe_search`
- The scraper timeout is set to 7.5 seconds (7500ms) by default
- API keys can be revoked at any time through the UI
- Future updates will include more open-source, self-hosted options for all components
- Additional customization options are planned, including:
  - Control over the number of links to scrape
  - Domain allowlist/blocklist for scraping
  - Custom scraping rules and filters
  - Advanced result filtering and ranking options
  - Rate limiting and request throttling controls 


# Message Search (https://www.librechat.ai/docs/features/search)

LibreChat has integrated **Meilisearch** to enhance the user experience by providing a fast and efficient way to search through past conversations. Meilisearch is a powerful, open-source search engine that is known for its speed and ease of use, making it an excellent choice for applications like LibreChat that require quick access to a large volume of data.

![Searching conversations for a banana](https://github.com/danny-avila/LibreChat/assets/32828263/60ad41b0-1869-4ee9-848b-502b3a5557b5)


The integration lets users:

- Search conversation titles, tags, and indexed message contents from the sidebar
- Open a conversation when either its metadata or one of its messages matches
- Use typo tolerance and fast as-you-type results across conversation history
- Search messages and shared-link candidates with page sizes above Meilisearch's old 20-result request default

LibreChat combines conversation-index and message-index matches before loading the visible conversation page. Searches remain bounded by Meilisearch's configured `pagination.maxTotalHits`; the default ceiling used by LibreChat queries is 1,000 hits. See the [Meilisearch Configuration Guide](/docs/configuration/meilisearch) for setup, synchronization, and reindexing behavior.

<Callout type="info" title="Keyword search, not semantic search">

Conversation search is **keyword-based**. It matches the words you type, so searching for "banana" finds messages containing "banana"; it will not find a message about "fruit" that never uses the word.

There is no vector or semantic search over conversation history. Semantic retrieval in LibreChat applies to **uploaded files**, not chat history: the [RAG API](/docs/features/rag_api) embeds documents you attach and retrieves them by meaning using PostgreSQL + pgvector. The two are separate systems, and enabling one does not affect the other.

</Callout>


# User Memory (https://www.librechat.ai/docs/features/memory)

## Overview

User Memory in LibreChat is a **key/value store** that persists user-specific information across conversations. Users can manage entries directly, an optional memory agent can extract updates from chats, and Agents can use memory tools when explicitly asked to remember or forget something.

<Callout type="info" title="Key/Value Store, Not Conversation Memory">
This is **not** semantic memory over your entire conversation history. It does not index, embed, or search past conversations. Instead, it maintains a structured set of key/value pairs (e.g., `user_preferences`, `learned_facts`) that are injected into each request as context. Think of it as a persistent notepad the AI reads before every response.

For context about previous messages within a single conversation, LibreChat already uses the standard message history window — that is separate from this feature.

</Callout>

<Callout type="important" title="⚠️ Configuration Required">
  Memory functionality must be explicitly configured in your `librechat.yaml` file to work. It is
  not enabled by default.
</Callout>

## Key Features

- **Optional Automatic Extraction**: Set `memory.agent.enabled: true` to run the configured memory agent with chat requests
- **Key/Value Storage**: Information is stored as structured key/value pairs, not as raw conversation logs
- **Manual Entries**: Users can manually add, edit, or remove memory entries directly, giving full control over what the AI remembers
- **User Control**: When enabled, users can toggle memory on/off for their individual chats
- **Customizable Keys**: Restrict what categories of information can be stored using `validKeys`
- **Token Management**: Set limits on memory usage to control costs
- **Agent Tools**: Agents can save or delete memories when the user explicitly requests it
- **Agent Partitions**: Keep an agent's memories separate from the user's shared personal pool

## Configuration

To enable memory features, you need to add the `memory` configuration to your `librechat.yaml` file:

```yaml filename="librechat.yaml"
version: 1.3.15
cache: true

memory:
  disabled: false # Set to true to completely disable memory
  personalize: true # Gives users the ability to toggle memory on/off, true by default
  tokenLimit: 2000 # Maximum tokens for memory storage
  maxInputTokens: 12000 # Maximum recent-chat tokens sent to the memory agent
  messageWindowSize: 5 # Number of recent messages to consider
  agent:
    enabled: true # Automatic extraction is opt-in
    provider: 'openAI'
    model: 'gpt-4'
```

The provider field should match the accepted values as defined in the [Model Spec Guide](/docs/configuration/librechat_yaml/object_structure/model_specs#endpoint).

**Note:** If you are using a custom endpoint, the endpoint value must match the defined custom endpoint name exactly.

See the [Memory Configuration Guide](/docs/configuration/librechat_yaml/object_structure/memory) for detailed configuration options.

## How It Works

<Callout type="note" title="Memory Agent Execution">
When `memory.agent.enabled: true`, the configured memory agent runs with chat requests. It executes concurrently with the main chat response — it begins before the main response starts and is limited to the duration of the main request plus up to 3 seconds after it finishes.

This means every message you send triggers the memory agent to:

<ol>
  <li>
    <strong>Read</strong> the current key/value store and inject relevant entries as context
  </li>
  <li>
    <strong>Analyze</strong> the recent message window for information worth storing or updating
  </li>
  <li>
    <strong>Write</strong> any new or modified entries back to the store
  </li>
</ol>
</Callout>

## Agent Memory

The Agents endpoint includes a `memory` capability. When memory is configured, the user has memory write permissions, and personalization is enabled, adding **Memory** to an agent gives it `set_memory` and `delete_memory` tools. The agent is instructed to use these tools only when the user explicitly asks it to remember, update, or forget something.

Agents use the user's shared personal memory pool by default. In the Agent Builder's Memory settings, enable **Keep memories separate for this agent** to isolate storage by user and agent. An isolated agent does not see existing personal memories or memories belonging to other isolated agents. Agent-scoped entries appear with the agent's name in the Memory panel and can be filtered by partition.

Agent partitions are anchored to Agent access. Reading, creating, or updating one requires the user to have Agents access and current view access to the referenced Agent; Agent managers retain their normal broader access. LibreChat rejects missing or inaccessible Agent IDs rather than treating them as arbitrary memory namespaces. Deletion may still target a partition whose Agent was removed so orphaned memories can be cleaned up.

The automatic memory agent and inline Agent memory tools are independent. `memory.agent.enabled: true` controls automatic extraction, while the Agents endpoint's `memory` capability controls whether saved Agents can manage memories during a conversation.

### 1. Key/Value Storage

Memory entries are stored as key/value pairs. When memory is enabled, the system can store entries such as:

- User preferences (communication style, topics of interest)
- Important facts explicitly shared by users
- Ongoing projects or tasks mentioned
- Any category you define via `validKeys`

Users can also **manually create, edit, and delete** memory entries through the interface, giving direct control over what the AI knows about them.

### 2. Context Window

The `messageWindowSize` parameter determines how many recent messages are analyzed for memory updates. This helps the memory agent decide what information is worth storing or updating in the key/value store.

The `maxInputTokens` parameter caps the recent-chat text sent to the automatic memory agent before extraction. If the selected message window is still too large, LibreChat preserves the newest context and omits earlier chat content before invoking the memory agent.

### 3. User Control

When `personalize` is set to `true`:

- Users see a memory toggle in their chat interface
- They can enable/disable memory for individual conversations
- Memory settings persist across sessions

### 4. Valid Keys

You can restrict what categories of information are stored by specifying `validKeys`:

```yaml filename="memory / validKeys"
memory:
  validKeys:
    - 'user_preferences'
    - 'conversation_context'
    - 'learned_facts'
    - 'personal_information'
```

## Best Practices

### 1. Token Limits

Set appropriate token limits to balance functionality with cost:

- Higher limits allow more comprehensive memory
- Lower limits reduce processing costs
- Consider your usage patterns and budget

### 2. Custom Instructions

When using `validKeys`, provide custom instructions to the memory agent:

```yaml filename="memory / agent with instructions"
memory:
  agent:
    enabled: true
    provider: 'openAI'
    model: 'gpt-4'
    instructions: |
      Store information only in the specified validKeys categories.
      Focus on explicitly stated preferences and important facts.
      Delete outdated or corrected information promptly.
```

### 3. Privacy Considerations

- Memory stores user information across conversations
- Ensure users understand what information is being stored
- Consider implementing data retention policies
- Provide clear documentation about memory usage

## Examples

### Basic Configuration

Enable memory with default settings:

```yaml filename="Basic memory config"
memory:
  tokenLimit: 2000
  maxInputTokens: 12000
  agent:
    enabled: true
    provider: 'openAI'
    model: 'gpt-4.1-mini'
```

### Advanced Configuration

Full configuration with all options:

```yaml filename="Advanced memory config"
memory:
  disabled: false
  validKeys: ['preferences', 'context', 'facts']
  tokenLimit: 3000
  maxInputTokens: 12000
  personalize: true
  messageWindowSize: 10
  agent:
    enabled: true
    provider: 'anthropic'
    model: 'claude-3-opus-20240229'
    instructions: 'Remember only explicitly stated preferences and key facts.'
    model_parameters:
      temperature: 0.3
```

For valid model parameters per provider, see the [Model Spec Preset Fields](/docs/configuration/librechat_yaml/object_structure/model_specs#preset-fields).

### Using Predefined Agents

Reference an existing agent by ID:

```yaml filename="Memory with agent ID"
memory:
  agent:
    enabled: true
    id: 'memory-specialist-001'
```

### Custom Endpoints with Memory

Memory fully supports custom endpoints, including those with custom headers and environment variables. When using a custom endpoint, header placeholders and environment variables are properly resolved during memory processing.

```yaml filename="librechat.yaml with custom endpoint for memory"

endpoints:
    custom:
        - name: 'Custom Memory Endpoint'
           apiKey: 'dummy'
           baseURL: 'https://api.gateway.ai/v1'
           headers:
             x-gateway-api-key: '${GATEWAY_API_KEY}'
             x-gateway-virtual-key: '${GATEWAY_OPENAI_VIRTUAL_KEY}'
             X-User-Identifier: '{{LIBRECHAT_USER_EMAIL}}'
             X-Application-Identifier: 'LibreChat - Test'
             api-key: '${TEST_CUSTOM_API_KEY}'
           models:
             default:
               - 'gpt-4o-mini'
               - 'gpt-4o'
             fetch: false

memory:
  disabled: false
  tokenLimit: 3000
  maxInputTokens: 12000
  personalize: true
  messageWindowSize: 10
  agent:
    enabled: true
    provider: 'Custom Memory Endpoint'
    model: 'gpt-4o-mini'
```

- All [custom endpoint headers](/docs/configuration/librechat_yaml/object_structure/custom_endpoint#headers) are supported

## Troubleshooting

### Memory Not Working

1. Verify memory is configured in `librechat.yaml`
2. Check that `disabled` is set to `false`
3. Ensure the configured agent/model is available
4. Verify users have enabled memory in their chat interface
5. For custom endpoints: ensure the `provider` name matches the custom endpoint `name` exactly

### High Token Usage

1. Reduce `tokenLimit` to control costs
2. Reduce `maxInputTokens` to cap how much recent chat is sent to the memory agent
3. Decrease `messageWindowSize` to analyze fewer messages
4. Use `validKeys` to restrict what gets stored
5. Review and optimize agent instructions

### Inconsistent Memory

1. Check if users are toggling memory on/off
2. Verify token limits aren't being exceeded
3. Ensure consistent agent configuration
4. Review stored memory for conflicts

### Custom Endpoint Authentication Issues

1. Verify environment variables are set correctly in your `.env` file
2. Ensure custom headers use the correct syntax (`${ENV_VAR}` for environment variables, `{{LIBRECHAT_USER_*}}` for user placeholders)
3. Check that the custom endpoint is working for regular chat completions before testing with memory
4. Review server logs for authentication errors from the custom endpoint API

## Future Improvements

The current implementation runs the memory agent on every chat request unconditionally. Planned improvements include:

- **Semantic Trigger for Writes**: Detect when a user has explicitly asked the model to remember something (e.g., "Remember that I prefer Python") and only run the memory write agent in those cases, reducing unnecessary processing on routine messages.
- **Vector Similarity Recall**: Instead of injecting all stored memory entries into every request, use vector embeddings to retrieve only the entries most relevant to the current conversation context, improving both efficiency and relevance.

## Related Features

- [Agents](/docs/features/agents) - Build custom AI assistants
- [Presets](/docs/user_guides/presets) - Save conversation settings
- [Fork Messages](/docs/features/fork) - Branch conversations while maintaining context


# RAG API (Chat with Files) (https://www.librechat.ai/docs/features/rag_api)

The **RAG (Retrieval-Augmented Generation) API** is a powerful tool that integrates with LibreChat to provide context-aware responses based on user-uploaded files.

It leverages LangChain, PostgresQL + PGVector, and Python FastAPI to index and retrieve relevant documents, enhancing the conversational experience.

**For further details, refer to the configuration guide provided here: [RAG API Configuration](/docs/configuration/rag_api)**

![image](https://github.com/danny-avila/LibreChat/assets/110412045/f1298f66-bf1d-4499-a582-23430b481f17)

---

**Currently, this feature is available through [Agents](/docs/features/agents), as well as through Custom Endpoints, OpenAI, Azure OpenAI, Anthropic, and Google.**

OpenAI Assistants have their own implementation of RAG through the "Retrieval" capability. Learn more about it [here.](https://platform.openai.com/docs/assistants/tools/knowledge-retrieval) 

It will still be useful to implement usage of the RAG API with the Assistants API since OpenAI charges for both file storage, and use of "Retrieval," and will be introduced in a future update.

**Still confused about RAG?** [Read the section I wrote below](#what-is-rag) explaining the general concept in more detail with a link to a helpful video.

## What is RAG?

RAG, or Retrieval-Augmented Generation, is an AI framework designed to improve the quality and accuracy of responses generated by large language models (LLMs). It achieves this by grounding the LLM on external sources of knowledge, supplementing the model's internal representation of information.


## Features

- **Document Indexing**: The RAG API indexes user-uploaded files, creating embeddings for efficient retrieval.
- **Semantic Search**: It performs semantic search over the indexed documents to find the most relevant information based on the user's input.
- **Context-Aware Responses**: By augmenting the user's prompt with retrieved information, the API enables LibreChat to generate more accurate and contextually relevant responses.
- **Asynchronous Processing**: The API supports asynchronous operations for improved performance and scalability.
- **Flexible Configuration**: It allows customization of various parameters such as chunk size, overlap, and embedding models.

### Key Benefits of RAG

1. **Access to up-to-date and reliable facts**: RAG ensures that the LLM has access to the most current and reliable information by retrieving relevant facts from an external knowledge base.
2. **Transparency and trust**: Users can access the model's sources, allowing them to verify the accuracy of the generated responses and build trust in the system.
3. **Reduced data leakage and hallucinations**: By grounding the LLM on a set of external, verifiable facts, RAG reduces the chances of the model leaking sensitive data or generating incorrect or misleading information.
4. **Lower computational and financial costs**: RAG reduces the need for continuous training and updating of the model's parameters, potentially lowering the computational and financial costs of running LLM-powered chatbots in an enterprise setting.

### How RAG Works

RAG consists of two main phases: retrieval and content generation.

1. **Retrieval Phase**: Algorithms search for and retrieve snippets of information relevant to the user's prompt or question from an external knowledge base. In an open-domain, consumer setting, these facts can come from indexed documents on the internet. In a closed-domain, enterprise setting, a narrower set of sources are typically used for added security and reliability.
2. **Generative Phase**: The retrieved external knowledge is appended to the user's prompt and passed to the LLM. The LLM then draws from the augmented prompt and its internal representation of its training data to synthesize a tailored, engaging answer for the user. The answer can be passed to a chatbot with links to its sources.

### Challenges and Ongoing Research

While RAG is currently one of the best-known tools for grounding LLMs on the latest, verifiable information and lowering the costs of constant retraining and updating, it's not perfect. Some challenges include:

1. **Recognizing unanswerable questions**: LLMs need to be explicitly trained to recognize questions they can't answer based on the available information. This may require fine-tuning on thousands of examples of answerable and unanswerable questions.
2. **Improving retrieval and generation**: Ongoing research focuses on innovating at both ends of the RAG process: improving the retrieval of the most relevant information possible to feed the LLM, and optimizing the structure of that information to obtain the richest responses from the LLM.

In summary, RAG is a powerful framework that enhances the capabilities of LLMs by grounding them on external, verifiable knowledge. It helps to ensure more accurate, up-to-date, and trustworthy responses while reducing the costs associated with continuous model retraining. As research in this area progresses, we can expect further improvements in the quality and efficiency of LLM-powered conversational AI systems.

For a more detailed explanation of RAG, you can watch this informative video by IBM on Youtube:

<iframe width="560" height="315" src="https://www.youtube.com/embed/T-D1OfcDW1M?si=SDVz1Fxsoi_z4S89" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen></iframe>


## Conclusion

The RAG API is a powerful addition to LibreChat, enabling context-aware responses based on user-uploaded files. By leveraging Langchain and FastAPI, it provides efficient document indexing, retrieval, and generation capabilities. With its flexible configuration options and seamless integration, the RAG API enhances the conversational experience in LibreChat.

For more detailed information on the RAG API, including API endpoints, request/response formats, and advanced configuration, please refer to the official RAG API documentation.


# Upload Files as Text (https://www.librechat.ai/docs/features/upload_as_text)

# Upload Files as Text

Ever wanted to hand a PDF, a code file, or a spreadsheet to the AI and just say _"read this"_? That's exactly what **Upload as Text** does.

You attach a file, LibreChat extracts the text from it, and the full content gets pasted straight into your conversation. The AI can then read every word of it — no plugins, no vector databases, no extra services to configure. It works out of the box.

<Callout type="info" title="Zero setup required">
  Upload as Text works immediately on any LibreChat instance. It uses built-in text parsing — you don't need OCR, a RAG pipeline, or any external service to get started.
</Callout>

---

## How to use it

<Steps>
  <Step>
    ### Click the attachment icon

    In the chat input bar, click the **paperclip** (📎) icon.
  </Step>
  <Step>
    ### Pick "Upload as Text"

    From the dropdown menu, select **Upload as Text**. This tells LibreChat to read the file contents rather than pass it as a raw attachment.
  </Step>
  <Step>
    ### Choose your file

    Select the file from your device. LibreChat will extract the text and embed it directly into your message.
  </Step>
  <Step>
    ### Ask your question

    Type your prompt as usual. The AI now has the full text of your file in context and can reference any part of it.
  </Step>
</Steps>

When you select several files, LibreChat skips individual duplicates and files over the per-file size limit, names the skipped files in a notice, and continues uploading the valid files. The file-count and total-batch-size limits still apply to the files that remain; if the surviving batch exceeds either limit, the batch is rejected together. Single-file behavior is unchanged.

<Callout type="warn" title="Don't see the option?">
  If "Upload as Text" doesn't appear, the `context` capability may have been disabled by your admin. It's on by default — but if the capabilities list was customized, `context` needs to be explicitly included. See the [configuration section](#the-context-capability) below.
</Callout>

---

## Paste long text as a file

In the normal message composer, LibreChat can turn a paste longer than 2,500 characters into an Upload as Text attachment. This keeps a large paste out of the input while still providing its contents to the model through the conversation context. The first attachment is named `pasted-text.txt`; additional pasted-text attachments receive numbered names.

The browser-local **Paste long text as a file** setting is enabled by default under **Settings > Chat > Sending**. Shorter pastes and text pasted while answering an Agent's **Ask User** form remain inline. If context uploads are unavailable, the text also remains inline. If an attachment upload fails, LibreChat restores the text to the composer when it can do so without overwriting a newer draft.

Pasted-text attachments follow the same file-size, token, and [`fileTokenLimit`](#token-limits-and-truncation) rules as other Upload as Text files.

---

## What happens under the hood

When you upload a file this way, LibreChat doesn't just dump raw bytes into the prompt. It runs through a processing pipeline to extract clean, readable text:

1. **MIME type detection** — LibreChat checks what kind of file you uploaded (PDF, image, audio, source code, etc.) by inspecting its MIME type.
2. **Method selection** — Based on the file type and what services are available, it picks the best extraction method using this priority:

<Tabs items={["Priority order", "Decision examples"]}>
  <Tabs.Tab>
    | Priority | Method | When it's used |
    |----------|--------|---------------|
    | 1st | **OCR** | File is an image or scanned document, _and_ OCR is configured |
    | 2nd | **STT** (Speech-to-Text) | File is audio, _and_ STT is configured |
    | 3rd | **Text parsing** | File matches a known text MIME type |
    | 4th | **Fallback** | None of the above matched — tries text parsing anyway |
  </Tabs.Tab>
  <Tabs.Tab>
    **A `.pdf` on an instance with OCR configured:**
    → OCR kicks in. Great for scanned docs and complex layouts.

    **A `.pdf` on a default instance (no OCR):**
    → Text parsing handles it. Works well for digitally-created PDFs.

    **A `.py` Python file:**
    → Straight to text parsing. Source code is already text — no conversion needed.

    **An `.mp3` on an instance with STT configured:**
    → Speech-to-Text transcribes it into text for the conversation.

    **A `.png` screenshot with no OCR configured:**
    → Falls back to text parsing (limited results — consider setting up OCR for images).
  </Tabs.Tab>
</Tabs>

3. **Token truncation** — The extracted text is trimmed to the `fileTokenLimit` (default: 100,000 tokens) so it doesn't blow past the model's context window.
4. **Prompt injection** — The text gets included in the conversation context, right alongside your message.

When you preview or download an Upload as Text attachment, LibreChat serves the extracted text stored with the file record. Downloads use a `.txt` filename even when OCR or another parser produced the stored text and there is no original backing file to stream.

---

## Which files are supported

<Tabs items={["Text & code", "Documents", "Images", "Audio"]}>
  <Tabs.Tab>
    These are parsed directly — they're already text, so no conversion is needed.

    - Plain text (`.txt`), Markdown (`.md`), CSV, JSON, XML, HTML, CSS
    - Programming languages — Python, JavaScript, TypeScript, Java, C#, PHP, Ruby, Go, Rust, Kotlin, Swift, Scala, Perl, Lua
    - Config files — YAML, TOML, INI
    - Shell scripts, SQL files
  </Tabs.Tab>
  <Tabs.Tab>
    Text parsing handles these out of the box. If OCR is configured, it takes over for better accuracy on complex layouts.

    - **PDF** — digital and scanned (scanned PDFs benefit from OCR)
    - **Word** — `.docx`, `.doc`
    - **PowerPoint** — `.pptx`, `.potx`, `.ppt`
    - **Excel** — `.xlsx`, `.xls`
    - **EPUB** books
  </Tabs.Tab>
  <Tabs.Tab>
    Images **require OCR** to produce useful text. Without it, results will be poor.

    - JPEG, PNG, GIF, WebP
    - HEIC, HEIF (Apple formats)
    - Screenshots, photos of documents, scanned pages
  </Tabs.Tab>
  <Tabs.Tab>
    Audio files **require STT** to be configured. There's no fallback — audio can't be "text parsed."

    - MP3, WAV, OGG, FLAC
    - M4A, WebM
    - Voice recordings, podcast clips
  </Tabs.Tab>
</Tabs>

LibreChat normalizes shell-script MIME variants reported by Chrome on Linux (`application/x-shellscript`) and libmagic (`text/x-shellscript`) to the canonical `application/x-sh` before checking the endpoint allowlist. Custom [`supportedMimeTypes`](/docs/configuration/librechat_yaml/object_structure/file_config#supportedmimetypes-3) rules must still permit `application/x-sh`.

---

## Upload as Text vs. other upload options

LibreChat has three ways to upload files. Each one works differently and suits different situations:

<Cards>
  <Card title="Upload as Text" href="#how-to-use-it">
    Extracts the full file content and drops it into the conversation. Best for smaller files where you want the AI to read everything — contracts, code files, articles. Works with all models, no extra services needed.
  </Card>
  <Card title="Upload for File Search (RAG)" href="/docs/features/rag_api">
    Indexes the file in a vector database and retrieves only the relevant chunks when you ask a question. Better for large files or collections of files where dumping everything into context would waste tokens. Requires the RAG API.
  </Card>
  <Card title="Standard Upload" href="/docs/features/agents">
    Passes the file directly to the model — used for vision models analyzing images, or code interpreter running scripts. No text extraction happens.
  </Card>
</Cards>

**Quick decision guide:**

| Situation | Best option |
|-----------|-------------|
| _"Read this 5-page contract and summarize it"_ | **Upload as Text** |
| _"I have 50 PDFs, find what mentions pricing"_ | **File Search (RAG)** |
| _"What's in this screenshot?"_ (vision model) | **Standard Upload** |
| _"Run this Python script"_ (code interpreter) | **Standard Upload** |
| _"Review this code file for bugs"_ | **Upload as Text** |
| _"Search through our company docs"_ | **File Search (RAG)** |

---

## The `context` capability

Under the hood, Upload as Text is powered by the **`context` capability**. This is what controls whether the feature appears in your chat UI.

<Callout type="info">
  The `context` capability is **enabled by default**. You only need to touch this if your admin has customized the capabilities list and accidentally left it out.
</Callout>

```yaml title="librechat.yaml"
endpoints:
  agents:
    capabilities:
      - "context"  # This is what enables "Upload as Text"
```

The same `context` capability also powers **Agent File Context** (uploading files through the Agent Builder to embed text into an agent's system instructions). The difference is _where_ the text ends up:

| | Upload as Text | Agent File Context |
|---|---|---|
| **Where** | Chat input (any conversation) | Agent Builder panel |
| **Scope** | Current conversation only | Persists in agent's instructions |
| **Use case** | One-off document questions | Building specialized agents with baked-in knowledge |

---

## Token limits and truncation

When a file is too long to fit in the model's context window, LibreChat truncates the extracted text to stay within bounds. This happens automatically — you don't need to worry about it, but it's good to know how it works.

```yaml title="librechat.yaml"
fileConfig:
  fileTokenLimit: 100000  # Default: 100,000 tokens
```

<Callout type="warn" title="Truncation means lost content">
  If your file exceeds the limit, the text is cut off at the end. If you're getting incomplete answers, this might be why. You can increase `fileTokenLimit`, but keep in mind that larger values use more tokens per message — which increases cost and may hit the model's own context limit.
</Callout>

**Rules of thumb:**
- 100k tokens ≈ a 300-page book (plenty for most use cases)
- If you're working with very large files, consider [File Search (RAG)](/docs/features/rag_api) instead — it only retrieves the relevant sections rather than stuffing everything into context

---

## Optional: boosting extraction with OCR

Text parsing works fine for digitally-created documents (PDFs saved from Word, code files, plain text). But if you're uploading **scanned documents, photos of pages, or images with text**, the built-in parser won't get great results.

That's where OCR comes in. When configured, LibreChat automatically uses OCR for file types that benefit from it — you don't need to do anything differently as a user.

<Accordions>
  <Accordion title="How to tell your admin to set up OCR">
    Point them to the [OCR configuration docs](/docs/features/ocr). The short version:

    ```yaml title="librechat.yaml"
    ocr:
      strategy: "mistral_ocr"
      apiKey: "${OCR_API_KEY}"
      baseURL: "https://api.mistral.ai/v1"
      mistralModel: "mistral-ocr-latest"
    ```

    Once configured, OCR automatically handles images and scanned PDFs — no changes needed on the user side.
  </Accordion>
  <Accordion title="How to tell your admin to set up STT (for audio files)">
    For transcribing audio uploads, the admin needs to configure a Speech-to-Text service. See the [STT configuration reference](/docs/configuration/librechat_yaml/object_structure/file_config#stt).
  </Accordion>
</Accordions>

---

## File handling configuration reference

This section is for admins who want to control which file types get processed by which method. The defaults work well — you only need to touch this if you want to fine-tune behavior.

<Accordions>
  <Accordion title="Full fileConfig example">
    ```yaml title="librechat.yaml"
    fileConfig:
      # Max tokens extracted from a single file before truncation
      fileTokenLimit: 100000

      # Files matching these MIME patterns use OCR (if configured)
      ocr:
        supportedMimeTypes:
          - "^image/(jpeg|gif|png|webp|heic|heif)$"
          - "^application/pdf$"
          - "^application/vnd\\.openxmlformats-officedocument\\.(wordprocessingml\\.document|presentationml\\.presentation|spreadsheetml\\.sheet)$"
          - "^application/vnd\\.ms-(word|powerpoint|excel)$"
          - "^application/epub\\+zip$"

      # Files matching these MIME patterns use text parsing
      text:
        supportedMimeTypes:
          - "^text/(plain|markdown|csv|json|xml|html|css|javascript|typescript|x-python|x-java|x-csharp|x-php|x-ruby|x-go|x-rust|x-kotlin|x-swift|x-scala|x-perl|x-lua|x-shell|x-sql|x-yaml|x-toml)$"

      # Files matching these MIME patterns use STT (if configured)
      stt:
        supportedMimeTypes:
          - "^audio/(mp3|mpeg|mpeg3|wav|wave|x-wav|ogg|vorbis|mp4|x-m4a|flac|x-flac|webm)$"
    ```

    **Priority reminder:** OCR > STT > text parsing > fallback.

    For the full reference, see [File Config Object Structure](/docs/configuration/librechat_yaml/object_structure/file_config).
  </Accordion>
</Accordions>

---

## Troubleshooting

<Accordions>
  <Accordion title="Upload as Text option not appearing">
    The `context` capability was likely removed from your configuration. Ask your admin to add it back:

    ```yaml
    endpoints:
      agents:
        capabilities:
          - "context"
    ```
  </Accordion>
  <Accordion title="File content looks wrong or is mostly empty">
    A few things to check:
    - **Scanned PDFs / images** — These need OCR to extract text properly. Without it, the parser might return garbage or nothing. Ask your admin to [configure OCR](/docs/features/ocr).
    - **Audio files** — These need STT. There's no text fallback for audio.
    - **Corrupted files** — Try opening the file locally to make sure it's not damaged.
    - **Unsupported format** — If the MIME type doesn't match any configured pattern, LibreChat attempts a text parse fallback, which may not work for binary formats.
  </Accordion>
  <Accordion title="Upload is rejected as an unsupported file type">
    LibreChat returns the rejected MIME type in the upload error. Check the active endpoint's [`fileConfig.endpoints.<endpoint>.supportedMimeTypes`](/docs/configuration/librechat_yaml/object_structure/file_config#supportedmimetypes-3) patterns and allow the canonical type you intend to accept. For shell scripts, use `application/x-sh`; LibreChat normalizes the common `application/x-shellscript` and `text/x-shellscript` variants before matching.
  </Accordion>
  <Accordion title="The AI seems to be missing part of my file">
    Your file probably exceeded the `fileTokenLimit`. The text was truncated.

    **Options:**
    - Ask your admin to increase `fileTokenLimit` in `librechat.yaml`
    - Use [File Search (RAG)](/docs/features/rag_api) instead, which retrieves relevant chunks rather than loading the entire file
    - Split the file into smaller parts and upload them separately
  </Accordion>
  <Accordion title="Images upload but the AI can't read the text in them">
    Without OCR, images are processed through text parsing, which can't actually "see" text in an image. You need OCR configured for this to work. See [OCR for Documents](/docs/features/ocr).

    Alternatively, use a **vision model** with standard file upload — the model itself can read text in images.
  </Accordion>
</Accordions>

---

## Related

- [OCR for Documents](/docs/features/ocr) — Set up optical character recognition for images and scans
- [RAG API (Chat with Files)](/docs/features/rag_api) — Semantic search over large document collections
- [Agents — File Context](/docs/features/agents#file-context) — Embed file content into an agent's system instructions
- [File Config reference](/docs/configuration/librechat_yaml/object_structure/file_config) — Full YAML schema for file handling


# OCR for Documents (https://www.librechat.ai/docs/features/ocr)

OCR (Optical Character Recognition) in LibreChat is an optional enhancement for text extraction from files.

### Upload as Text

The "Upload as Text" feature (from the chat) works the same way:

- Files matching `fileConfig.ocr.supportedMimeTypes` use OCR if available
- Falls back to text parsing if OCR is not configured
- Especially useful for images with text, scanned documents, and complex PDFs
- Processing priority: **OCR > STT > text parsing**
- See the [Upload as Text](/docs/features/upload_as_text) documentation for details.

### File Context (for agents)

When you upload files through the Agent Builder's File Context section:

1. Text is extracted using text parsing by default (OCR/STT if configured and file matches)
2. Extracted text is stored as part of the agent's system instructions
3. Agent can reference this context in all conversations
4. **OCR service is optional** - the feature works without it using text parsing

Files uploaded as "File Context" are processed to extract text, which is then added to the Agent's system instructions. This is ideal for documents, code files, PDFs, or images with text where you need the full text content to be included in the agent's instructions.

**Note:** The extracted text is included in the agent's system instructions.

## Optional OCR Configuration

Both Agent File Context and Upload as Text work out-of-the-box using text parsing. To enhance extraction quality for images and scanned documents, you can optionally configure an OCR service:

```yaml
# librechat.yaml
endpoints:
  agents:
    capabilities:
      - "context"  # Enables both agent file context and upload as text
      - "ocr"      # Optionally enhances both with OCR

ocr:
  strategy: "mistral_ocr"
  apiKey: "${OCR_API_KEY}"
  baseURL: "https://api.mistral.ai/v1"
  mistralModel: "mistral-ocr-latest"
```

**Note:** The `context` capability is enabled by default. You only need to configure OCR (the `ocr` capability) if you want enhanced extraction quality for images and scanned documents.

OCR connections block private, loopback, link-local, and cloud-metadata destinations by default. For a trusted private OCR service, add its exact private host and port to [`ocr.allowedAddresses`](/docs/configuration/librechat_yaml/object_structure/ocr#allowedaddresses). This is an exemption from SSRF protection, so list only infrastructure you control.

## Overview of OCR Capabilities

OCR functionality in LibreChat allows:

- Extract text from images and documents
- Maintain document structure and formatting
- Process complex layouts including multi-column text
- Handle tables, equations, and other specialized content
- Work with multilingual content

## OCR Strategies

LibreChat supports multiple OCR strategies to meet different deployment needs and requirements. Choose the strategy that best fits your infrastructure and compliance requirements.

### 1. Mistral OCR (Default)

The default strategy uses Mistral's cloud API service directly. This is the simplest setup and requires only an API key from Mistral.

**Environment Variables:**
```.env
# `.env`
OCR_API_KEY=your-mistral-api-key
# OCR_BASEURL=https://api.mistral.ai/v1 # this is the default value
```

**Configuration:**
```yaml
# `librechat.yaml`
ocr:
  mistralModel: "mistral-ocr-latest"       # Optional: Specify Mistral model, defaults to "mistral-ocr-latest"
  apiKey: "your-mistral-api-key"           # Optional: Defaults to OCR_API_KEY env variable
  baseURL: "https://api.mistral.ai/v1"     # Optional: Defaults to OCR_BASEURL env variable, or Mistral's API if no variable set
  strategy: "mistral_ocr"                  # Optional: Defaults to "mistral_ocr"
```

**Key Features:**
- **Document Structure Preservation**: Maintains formatting like headers, paragraphs, lists, and tables
- **Multilingual Support**: Processes text in multiple languages and scripts
- **Complex Layout Handling**: Handles multi-column text and mixed content
- **Mathematical Expression Recognition**: Accurately processes equations and formulas
- **High-Speed Processing**: Processes up to 2000 pages per minute

**Considerations:**
- **Cost**: Using Mistral OCR may incur costs as it's a paid API service (though free trials may be available)
- **Data Privacy**: Data processed through Mistral OCR is subject to Mistral's cloud environment and their terms of service
- **Document Limitations**: 
  - Maximum file size: 50 MB
  - Maximum document length: 1,000 pages

### 2. Azure Mistral OCR

For organizations using Azure AI Foundry, you can deploy Mistral OCR models to your Azure infrastructure. Currently, the **Mistral OCR 2503** model is available for Azure deployment.

**Configuration:**
```yaml
# `librechat.yaml`
ocr:
  mistralModel: "deployed-mistral-ocr-2503"              # Should match your Azure deployment name
  apiKey: "${AZURE_MISTRAL_OCR_API_KEY}"                 # Reference to your Azure API key in .env
  baseURL: "https://your-deployed-endpoint.models.ai.azure.com/v1"  # Your Azure endpoint
  strategy: "azure_mistral_ocr"                          # Use Azure strategy
```

**Azure Model Information:**
You can explore the latest Mistral OCR model available on Azure AI Foundry here (requires Azure subscription):

https://ai.azure.com/explore/models/mistral-ocr-2503

### 3. Google Vertex AI Mistral OCR

For organizations using Google Cloud Platform, you can deploy Mistral OCR models to your Google Cloud Vertex AI infrastructure.

**Environment Variables:**
```bash
# `.env`
# Option 1: File path
GOOGLE_SERVICE_KEY_FILE=/path/to/your/service-account-key.json

# Option 2: URL to fetch the key
GOOGLE_SERVICE_KEY_FILE=https://your-secure-server.com/service-account-key.json

# Option 3: Base64 encoded JSON
GOOGLE_SERVICE_KEY_FILE=eyJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsICJwcm9qZWN0X2lkIjogInlvdXItcHJvamVjdC1pZCIsIC4uLn0=

# Option 4: Raw JSON string
GOOGLE_SERVICE_KEY_FILE='{
  "type": "service_account",
  "project_id": "your-project-id",
  "private_key_id": "...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
  "client_email": "...",
  "client_id": "...",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "..."
}'
```

**Configuration:**
```yaml
# `librechat.yaml`
ocr:
  mistralModel: "mistral-ocr-2505"                        # Model name as deployed in Vertex AI
  strategy: "vertexai_mistral_ocr"                       # Use Google Vertex AI strategy
```

**Setup Requirements:**
1. Deploy a Mistral OCR model to Google Vertex AI (e.g., mistral-ocr-2505)
2. Create a service account with appropriate permissions to access the Vertex AI endpoint
3. Download the service account JSON key file
4. Set the `GOOGLE_SERVICE_KEY_FILE` environment variable using one of the supported methods

### 4. Custom OCR (Planned)

Support for custom OCR providers and user-defined strategies is planned for future releases.

### 5. Upload Files to Provider (Direct)

For supported LLM Providers (**OpenAI, AzureOpenAI, Anthropic, Google, and AWS Bedrock**) and their respective models, files can now be sent directly to the provider APIs as message attachments,
allowing the provider to use their own native OCR implementations to parse files using the `Upload to Provider` option in the file attachment dropdown menu. 

Currently all five of the aforementioned providers offer support for images and PDFs, with Google also including support for audio and video files when used in conjunction with compatible multimodal models. AWS Bedrock additionally supports CSV, DOC, DOCX, XLS, XLSX, HTML, TXT, and Markdown documents.

<Callout type="note" title="Azure OpenAI PDF Upload Caveat" emoji='✏️'>
For **Azure OpenAI** endpoints, the Upload to Provider option for PDF files is only available when using the Responses API. Azure OpenAI's Chat Completions API supports images but does not support PDF file attachments.

If you do not see 'Upload to Provider' as an option for PDFs in your chat's attachment dropdown menu with Azure OpenAI, ensure that the Responses API parameter is enabled in the Parameters panel.

Note: Standard OpenAI endpoints support PDF uploads in both Chat Completions and Responses APIs.
</Callout>

<Callout type="note" title="AWS Bedrock Document Upload Limits" emoji='🪨'>
**AWS Bedrock** supports document uploads via the Converse API for the following formats:
**PDF, CSV, DOC, DOCX, XLS, XLSX, HTML, TXT, and Markdown (.md)**

Constraints:
- The default maximum file size is **4.5 MB**
- When `fileConfig` does not set a smaller limit, Claude 4+ PDFs and Amazon Nova PDFs or DOCX files can be up to **32 MB**
- File names are sanitized to conform to Bedrock's naming requirements (alphanumeric, spaces, hyphens, parentheses, square brackets; max 200 characters)

For more details on Bedrock configuration, see the [AWS Bedrock setup guide](/docs/configuration/pre_configured_ai/bedrock).
</Callout>

## Detailed Configuration

For additional, detailed configuration options, see the [OCR Config Object Structure](/docs/configuration/librechat_yaml/object_structure/ocr).

## OCR Processing Configuration

Control which file types are processed with OCR using `fileConfig`:

```yaml
fileConfig:
  ocr:
    supportedMimeTypes:
      - "^image/(jpeg|gif|png|webp|heic|heif)$"
      - "^application/pdf$"
      - "^application/vnd\\.openxmlformats-officedocument\\.(wordprocessingml\\.document|presentationml\\.(presentation|template)|spreadsheetml\\.sheet)$"
      - "^application/vnd\\.ms-(word|powerpoint|excel)$"
      - "^application/epub\\+zip$"
```

Files matching these patterns will use OCR when:
- Uploaded to agent file context (always, if OCR is configured)
- Uploaded as text in chat (if OCR is configured; otherwise falls back to text parsing)

For more details on file processing configuration, see [File Config Object Structure](/docs/configuration/librechat_yaml/object_structure/file_config).

## Use Cases for Agent File Context

Agent File Context is ideal for:

- **Persistent Agent Knowledge**: Add documentation, policies, or reference materials to an agent's system instructions
- **Specialized Agents**: Create agents with domain-specific knowledge from documents
- **Document-Based Assistants**: Build agents that always reference specific manuals or guides
- **Code Files**: Include code examples or libraries in agent instructions
- **Structured Data**: Add CSV, JSON, or other structured data for the agent to reference

When OCR is configured, File Context also handles:
- **Scanned Document Processing**: Extract and store text from images or scanned PDFs
- **Image Text Extraction**: Extract text from screenshots or photos of documents

For temporary document questions in chat, see [Upload as Text](/docs/features/upload_as_text).

## Limitations

- Text extraction accuracy may vary depending on file type, image quality, document complexity, and text clarity
- Some specialized formatting or unusual layouts might not be perfectly preserved
- Very large documents may be truncated due to token limitations of the underlying AI models
- For best results with images and scanned documents, configure an OCR service

## Future Enhancements

LibreChat plans to expand OCR capabilities in future releases:

- Support for custom OCR providers
- A `user_provided` strategy option that will allow users to choose their preferred OCR service
- Integration with open-source OCR solutions
- Enhanced document processing options
- More granular control over OCR settings
- Mistral plans to make their OCR API available through their cloud partners, such as GCP and AWS, and enterprise self-hosting for organizations with stringent data privacy requirements ([source](https://mistral.ai/fr/news/mistral-ocr))
- LibreChat currently does not include the parsed image content from the OCR process in its responses, even though services like [Mistral's OCR API may provide](https://docs.mistral.ai/api/endpoint/ocr) these in the result. This feature may be supported in future updates.

---

For more information on configuring OCR, see the [OCR Config Object Structure](/docs/configuration/librechat_yaml/object_structure/ocr).


# Image Generation & Editing (https://www.librechat.ai/docs/features/image_gen)

LibreChat ships with built-in image tools that you add to an [Agent](/docs/features/agents). Each tool has its own model, price point, and setup, usually just an API key or a URL. There is no separate image page: you generate or edit images by chatting with an Agent that has an image tool enabled.

<Callout type="info" title="How image generation works">
Upload an image when you want an edit, or send a plain text prompt when you want a new image. Generated images follow the configured [`fileStrategy`](/docs/configuration/librechat_yaml/object_structure/config#filestrategy) and the tool output is sent to the LLM as part of the chat context immediately after generation.
</Callout>

## Quick Start

Get image generation working in a few minutes with OpenAI Image Tools.

<Steps>
<Step>

**Create an agent.** Select **Agents** from the endpoint menu, open the Agent Builder from the side panel, and create a new agent. Give it a name like "Image Creator".

</Step>
<Step>

**Add OpenAI Image Tools.** Open the agent's **Tools** list, select **OpenAI Image Tools**, and save the agent. This adds both image generation and image editing capabilities.

</Step>
<Step>

**Set your API key.** Add the following to your `.env` file:

```bash filename=".env"
IMAGE_GEN_OAI_API_KEY=sk-your-openai-api-key
# Optional; defaults to gpt-image-1
IMAGE_GEN_OAI_MODEL=gpt-image-1
```

</Step>
<Step>

**Restart and test.** Restart LibreChat, then send a message like "Generate an image of a sunset over mountains" to your agent.

| Deployment | Command                                       |
| ---------- | --------------------------------------------- |
| Docker     | `docker compose down && docker compose up -d` |
| Local      | Stop (Ctrl+C) then `npm run backend`          |

</Step>
</Steps>

<Callout type="info" title="Good to know">
- API keys can be omitted to let users enter their own key from the UI.
- Image outputs are sent to the LLM only immediately after generation, not on every message. The LLM otherwise gets vision context only from images attached to user messages. See [Image Storage and Handling](#image-storage-and-handling).
- MCP server tools can also output images, though they may not always use the correct format. See the [MCP section](#model-context-protocol-mcp).
</Callout>

## OpenAI Image Tools

"OpenAI Image Tools" is an agent toolkit made up of two separate tools:

- **Image Generation** creates brand-new images from text prompts (no upload required).
- **Image Editing** edits or remixes images you uploaded: change colors, add objects, extend the canvas, and more.

Both default to **GPT-Image-1** for instruction following, text rendering, detailed editing, and real-world knowledge. Use `IMAGE_GEN_OAI_MODEL` to choose a different OpenAI image model when your deployment supports it. See OpenAI's [Image Generation documentation](https://platform.openai.com/docs/guides/image-generation?image-generation-model=gpt-image-1) for more details.

### Generation vs. Editing

| Use case                | Invokes              |
| ----------------------- | -------------------- |
| "Start from scratch"    | **Image Generation** |
| "Use existing image(s)" | **Image Editing**    |

Both tools are always available, and the agent chooses the appropriate one based on the request:

- **Image Generation** creates new images from text descriptions only.
- **Image Editing** modifies or remixes existing images using their image IDs. These can be images from the current message or previously generated and referenced images. The LLM keeps track of image IDs as long as they remain in the context window and includes them in the tool output.

<Callout type="warning" title="Image editing relies on image IDs">
- Image IDs are retained in the chat history. When files are uploaded to the current request, their IDs are added to the LLM's context before any tokens are generated.
- Previously referenced or generated image IDs can be used for editing as long as they remain within the context window. The LLM includes any relevant IDs in the `image_ids` array when calling the editing tool.
- You can attach previously uploaded images from the side panel without uploading them again. This also gives a vision model the image context, which can help inform the `prompt` for the editing tool.
</Callout>

### Parameters

**Image Generation**

- **prompt**: text description (required)
- **size**: `auto` (default), `1024x1024` (square), `1536x1024` (landscape), or `1024x1536` (portrait)
- **quality**: `auto` (default), `high`, `medium`, or `low`
- **background**: `auto` (default), `transparent`, or `opaque` (transparent requires PNG or WebP format)

**Image Editing**

- **image_ids**: array of image IDs to use as reference for editing (required)
- **prompt**: text description of the changes (required)
- **size**: `auto` (default), `1024x1024`, `1536x1024`, `1024x1536`, `256x256`, or `512x512`
- **quality**: `auto` (default), `high`, `medium`, or `low`

### Setup

Create or reuse an OpenAI key and add it to `.env`, then add "OpenAI Image Tools" to your agent's **Tools** list:

```bash filename=".env"
IMAGE_GEN_OAI_API_KEY=sk-...
# optional extras
IMAGE_GEN_OAI_MODEL=gpt-image-1
IMAGE_GEN_OAI_BASEURL=https://...
```

For Azure OpenAI deployments, first request access at https://aka.ms/oai/gptimage1access, then add your credentials to `.env`:

```bash filename=".env"
IMAGE_GEN_OAI_API_KEY=your-api-key
# optional extras
IMAGE_GEN_OAI_MODEL=gpt-image-1
IMAGE_GEN_OAI_BASEURL=https://deploymentname.openai.azure.com/openai/deployments/gpt-image-1/
IMAGE_GEN_OAI_AZURE_API_VERSION=2025-04-01-preview
```

### Advanced Configuration

Customize the tool descriptions and prompt guidance with these environment variables:

```bash filename=".env"
# Image Model
IMAGE_GEN_OAI_MODEL=gpt-image-1

# Image Generation Tool Descriptions
IMAGE_GEN_OAI_DESCRIPTION=...
IMAGE_GEN_OAI_PROMPT_DESCRIPTION=...

# Image Editing Tool Descriptions
IMAGE_EDIT_OAI_DESCRIPTION=...
IMAGE_EDIT_OAI_PROMPT_DESCRIPTION=...
```

### Pricing

See the [GPT-Image-1 pricing page](https://platform.openai.com/docs/models/gpt-image-1) and [Image Generation documentation](https://platform.openai.com/docs/guides/image-generation?image-generation-model=gpt-image-1#cost-and-latency) for image generation costs.

## Gemini Image Tools

Gemini Image Tools integrate Google's latest image generation models, supporting both text-to-image generation and image context-aware editing.

- **Text-to-image generation**: create high-quality images from detailed text descriptions.
- **Image context support**: use existing images as context or inspiration for new generations.
- **Image editing**: generate new images based on modifications to existing ones (include the original image ID).
- **Multiple models**: choose `gemini-2.5-flash-image` (default) or `gemini-3-pro-image-preview`.
- **Dual API support**: works with both simple Gemini API keys and Google Cloud Vertex AI.

### Parameters

- **prompt**: detailed text description of the desired image (required, up to 32,000 characters)
- **image_ids**: optional array of image IDs to use as visual context for generation

### Setup

For the Gemini API, get a key from [Google AI Studio](https://aistudio.google.com/app/apikey):

```bash filename=".env"
GEMINI_API_KEY=your_api_key_here
```

For Vertex AI (Google Cloud users with Vertex AI access):

```bash filename=".env"
GOOGLE_SERVICE_KEY_FILE=/path/to/service-account.json
GOOGLE_CLOUD_LOCATION=us-central1  # optional, default: global
```

### Model Selection

```bash filename=".env"
# Default model (fast and efficient)
GEMINI_IMAGE_MODEL=gemini-2.5-flash-image

# Higher quality model
GEMINI_IMAGE_MODEL=gemini-3-pro-image-preview
```

### Advanced Configuration

Customize tool descriptions via environment variables:

```bash filename=".env"
GEMINI_IMAGE_GEN_DESCRIPTION=...
GEMINI_IMAGE_GEN_PROMPT_DESCRIPTION=...
GEMINI_IMAGE_IDS_DESCRIPTION=...
```

More details are in the dedicated [Gemini Image Gen guide](/docs/configuration/tools/gemini_image_gen).

## DALL·E (legacy)

DALL·E provides legacy image generation using OpenAI's `dall-e-3` image model.

### Parameters

- **prompt**: text description of the desired image (required, up to 4000 characters)
- **style**: `vivid` (hyper-real, dramatic, default) or `natural` (less hyper-real)
- **quality**: `standard` (default) or `hd`
- **size**: `1024x1024` (default, square), `1792x1024` (wide), or `1024x1792` (tall)

### Setup

```bash filename=".env"
# Required
DALLE_API_KEY=sk-...  # or DALLE3_API_KEY=sk-...

# Optional
DALLE_REVERSE_PROXY=https://...  # Alternative endpoint
DALLE3_BASEURL=https://...  # For Azure or custom endpoints
DALLE3_AZURE_API_VERSION=2023-12-01-preview  # For Azure deployments
DALLE3_SYSTEM_PROMPT=...  # Custom system prompt for DALL·E
```

Enable the **DALL·E** tool for the agent and start prompting.

### Advanced Configuration

For Azure OpenAI deployments, configure the base URL and API version:

```bash filename=".env"
DALLE3_BASEURL=https://your-resource-name.openai.azure.com/openai/deployments/your-deployment-name
DALLE3_AZURE_API_VERSION=2023-12-01-preview
DALLE3_API_KEY=your-azure-api-key
```

### Pricing

See the [DALL-E pricing page](https://platform.openai.com/docs/models/dall-e-3) and [Image Generation documentation](https://platform.openai.com/docs/guides/image-generation?image-generation-model=dall-e-3) for image generation costs.

## Stable Diffusion (local)

Run images entirely on your own machine or server. Point LibreChat at any Automatic1111 (or compatible) endpoint and you're set.

### Parameters

- **prompt**: detailed keywords describing desired elements in the image (required)
- **negative_prompt**: keywords describing elements to exclude from the image (required)

The Stable Diffusion implementation uses these fixed default parameters, which produce good results for most use cases:

- cfg_scale: 4.5
- steps: 22
- width: 1024
- height: 1024

### Setup

No API key is required, just the reachable URL of your Automatic1111 WebUI:

```bash filename=".env"
SD_WEBUI_URL=http://127.0.0.1:7860  # URL to your Automatic1111 WebUI
```

More details on setting up Automatic1111 are in the dedicated [Stable Diffusion guide](/docs/configuration/tools/stable_diffusion).

## Flux

Cloud generator with an emphasis on speed and optional fine-tuned models.

- Fast cloud-based image generation
- Support for fine-tuned models
- Multiple quality levels and aspect ratios
- Raw mode for less processed, more natural-looking images

### Parameters

The Flux tool supports three main actions:

1. **generate**: create a new image from a text prompt
2. **generate_finetuned**: create an image using a fine-tuned model
3. **list_finetunes**: list available custom models for the user

More details are in the dedicated [Flux guide](/docs/configuration/tools/flux#parameters).

### Setup

Choose the **Flux** tool inside the agent. Prompts are plain text, and one call produces one image.

```bash filename=".env"
FLUX_API_KEY=flux_live_...
FLUX_API_BASE_URL=https://api.us1.bfl.ai   # default is fine for most users
```

### Pricing

See the [Flux pricing page](https://docs.bfl.ml/pricing/) for image generation costs.

## Model Context Protocol (MCP)

Image outputs are supported from MCP servers. For example, the [Puppeteer MCP Server](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/puppeteer) can generate screenshots of web pages, which output the image in the expected format and are treated the same as LibreChat's built-in image tools.

<Callout type="warning" title="MCP image support is still emerging">
- The examples below assume LibreChat runs outside of Docker, directly using Node.js. The Model Context Protocol is a relatively new framework, and many developers are still learning how to serve their systems with uv/node for scalable distribution.
- Few image-generating servers exist, and many have yet to adopt the correct response format for images.
- While many MCP servers function well within Docker, the following examples do not, or not without more advanced configurations, showing some of the current inconsistency between MCP servers.
</Callout>

```yaml filename="librechat.yaml"
mcpServers:
  puppeteer:
    command: npx
    args:
      - -y
      - '@modelcontextprotocol/server-puppeteer'
```

The following is an example of an [Image Generation server](https://github.com/GongRzhe/Image-Generation-MCP-Server) that outputs images using the [Replicate API](https://replicate.com/account/api-tokens), but returns URLs of the images, which doesn't conform to MCP's image response standard.

<Callout type="info" title="Global install required">
For this particular server, install the `@gongrzhe/image-gen-server` package globally with `npm install -g @gongrzhe/image-gen-server`, then point to the package's compiled files as shown below.
</Callout>

```yaml filename="librechat.yaml"
mcpServers:
  image-gen:
    command: 'node'
    # First, install the package globally using npm:
    # `npm install -g @gongrzhe/image-gen-server`
    # Then, point to the location of the installed package,
    # which you can find by running `npm root -g`
    args:
      - '{REPLACE_WITH_NODE_MODULES_LOCATION}/@gongrzhe/image-gen-server/build/index.js'
      # Example with output from `npm root -g`:
      # - "/home/danny/.nvm/versions/node/v24.16.0/lib/node_modules/@gongrzhe/image-gen-server/build/index.js"
    env:
      # Do not hardcode the API token here, use the environment variable instead
      # The following will pick up the token from your .env file or environment
      REPLICATE_API_TOKEN: '${REPLICATE_API_TOKEN}'
      MODEL: 'google/imagen-3'
```

## Image Storage and Handling

All generated images are:

1. Saved according to the configured [`fileStrategy`](/docs/configuration/librechat_yaml/object_structure/config#filestrategy)
2. Displayed directly in the chat interface
3. Sent to the LLM as part of the immediate chat context following generation

A few caveats apply to that last point:

- This may cause issues with an LLM that does not support image inputs. An option to disable the behavior per agent is planned.
- Outputs are sent to the LLM only upon generation, not on every message.
- To include an image in later turns, attach it to the message from the side panel.
- In short, the LLM gets vision context only from images attached to user messages, and from generations or edits immediately after they happen.

## Proxy Support

All image generation tools support proxy configuration through the `PROXY` environment variable:

```bash filename=".env"
PROXY=http://proxy-url:port
```

When `PROXY` is unset, supported server-side clients honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`/`no_proxy`.

## Error Handling

If a tool encounters an error, it returns a message explaining what went wrong. Common issues include:

- Invalid API key
- API unavailability
- Content policy violations
- Proxy/network issues
- Invalid parameters
- Unsupported image payload (see [Image Storage and Handling](#image-storage-and-handling) above)

## Prompting

You can customize the prompts for [OpenAI Image Tools](#advanced-configuration) and [DALL·E](#advanced-configuration-2), but the following tips inform the default prompts the tools supply, which is useful to know for your own writing:

1. Start with the **subject** and **style** (photo, oil painting, etc.).
2. Add **composition** and **camera/medium** ("wide-angle shot of…", "watercolour…").
3. Mention **lighting and mood** ("golden hour", "dramatic shadows").
4. Finish with **detail keywords** (textures, colours, expressions).
5. Keep negatives positive: describe what should be included, not what to avoid.

Example:

> A cinematic photo of an antique library bathed in warm afternoon sunlight. Tall wooden shelves overflow with leather-bound books, and dust particles shimmer in the light. A single green-shaded banker's lamp illuminates an open atlas on a polished mahogany desk in the foreground. 85 mm lens, shallow depth of field, rich amber tones, ultra-high detail.

## Related Pages

<Cards num={3}>
  <Cards.Card title="Agents" href="/docs/features/agents" arrow>
    Create and configure AI agents with custom tools
  </Cards.Card>
  <Cards.Card title="MCP Servers" href="/docs/features/mcp" arrow>
    Bring your own tools via Model Context Protocol
  </Cards.Card>
  <Cards.Card title="Gemini Image Tools" href="/docs/configuration/tools/gemini_image_gen" arrow>
    Detailed setup guide for Google Gemini image generation
  </Cards.Card>
</Cards>


# Resumable Streams (https://www.librechat.ai/docs/features/resumable_streams)

Resumable streams let an in-progress AI response survive a dropped connection. If the network drops, the browser refreshes, or you switch tabs or devices, LibreChat reconstructs the content that was already streamed and continues from where it left off. The same mechanism keeps multiple viewers of one conversation in sync.

## What You Get

- **No lost responses.** Network drops, browser refreshes, and server restarts do not discard streamed content.
- **Tabs stay in sync.** Open one conversation in two browser tabs and both receive the same updates in real time.
- **Switch devices mid-stream.** Start a generation on your desktop and pick up the result on your phone.
- **Background generations.** Start a long response, move to another tab or app, and the full response is there when you return.
- **Shared conversations.** Every viewer of a shared chat sees content stream in at the same time.

## How It Works

When you send a message, LibreChat creates a generation job that records every streamed delta. If the connection breaks:

1. The client detects the disconnection.
2. On reconnect, the server rebuilds the content streamed so far from the job's recorded deltas.
3. The missing content is delivered in a single sync event.
4. Streaming continues from the current position.

This runs automatically and requires no user action.

## Deployment Modes

LibreChat ships with two backends for resumable streams.

### Single-Instance Mode (default)

Stores stream state in memory and uses a Node.js `EventEmitter` for pub/sub. This is the default and needs no configuration. It covers local development, single-server deployments, and Docker Compose setups.

### Redis Mode (production)

Uses Redis Streams and Pub/Sub so stream state is shared across instances. Use it for horizontally scaled, load-balanced, or high-availability deployments, including Kubernetes clusters. With Redis, a generation started on one instance can resume on another, which keeps active streams alive through rolling deployments and auto-scaling.

<Callout type="info" title="Single instance? You likely don't need Redis here">
In-memory mode handles everything for a single LibreChat instance. Redis becomes relevant once you run multiple instances behind a load balancer. Redis is still useful for caching and session storage in single-instance deployments, just not specifically for resumable streams.
</Callout>

## Configuration

### Enable Redis Streams

Setting `USE_REDIS=true` makes resumable streams use Redis automatically. Use `USE_REDIS_STREAMS` to control it explicitly.

```bash filename=".env"
USE_REDIS=true
REDIS_URI=redis://localhost:6379
# Resumable streams use Redis automatically when USE_REDIS=true.
# Set USE_REDIS_STREAMS to control it explicitly:
USE_REDIS_STREAMS=true
```

### Redis Cluster

For a Redis Cluster, enable cluster mode and list the nodes in `REDIS_URI`.

```bash filename=".env"
USE_REDIS_STREAMS=true
USE_REDIS_CLUSTER=true
REDIS_URI=redis://node1:7001,redis://node2:7002,redis://node3:7003
```

LibreChat uses hash-tagged keys so that multi-key operations land on the same cluster slot.

For high-token-rate deployments, `STREAM_DELTA_COALESCE_MS=25` can batch Redis delta publications and reduce Redis work at the cost of up to 25 ms of delivery latency. Leave it unset or set it to `0` to disable batching, and enable it only after every replica supports batch frames. See [Stream Delta Coalescing](/docs/configuration/redis#stream-delta-coalescing).

## What Gets Reconstructed

On reconnect, LibreChat aggregates the recorded delta events to rebuild:

- Message content (text, tool calls, citations)
- Agent run steps and intermediate reasoning
- Metadata and state information

The storage mechanism depends on the deployment mode:

| Component | Storage Mechanism |
|-----------|-------------------|
| Chunks | Redis Streams (`XADD`/`XRANGE`) |
| Job metadata | Redis Hash structures |
| Real-time events | Redis Pub/Sub channels |
| Expiration | Automatic TTL after stream completion |

LibreChat applies a few optimizations to keep this cheap:

- **Memory-first recovery.** Reconnecting to the same instance reads from local cache, avoiding a Redis round trip.
- **Cleanup on access.** Stale job entries are removed during queries, and completed streams expire automatically.
- **Garbage-collected storage.** In-memory mode stores stream graphs with `WeakRef`, so they are collected once a conversation ends.

## Testing

To confirm the feature is working, start a streaming conversation with any model, then try one of:

- **Tabs.** Open the same chat in a second tab; both should sync.
- **Disconnect.** Drop the network briefly, then reconnect.
- **Navigation.** Navigate away mid-stream, then return.

Each case should produce the complete response with no missing content.

## Troubleshooting

**Streams not resuming.** Confirm Redis is reachable and that `USE_REDIS_STREAMS` is set.

```bash
docker exec -it librechat-redis redis-cli ping
# Expected: PONG

echo $USE_REDIS_STREAMS
```

**Content appears duplicated.** This usually means a client version mismatch. Update to the latest version of LibreChat.

**High memory use in single-instance mode.** Completed streams are garbage collected. If memory stays high, look for very long-running streams that never complete or streams that errored without cleaning up.

## Related Documentation

- [Redis Configuration](/docs/configuration/redis): setting up Redis for caching and horizontal scaling
- [Agents](/docs/features/agents): AI agents with tool use
- [Docker Deployment](/docs/local/docker): container-based deployment

For implementation details, see [PR #10926](https://github.com/danny-avila/LibreChat/pull/10926).


# Smooth Streaming (https://www.librechat.ai/docs/features/smooth_streaming)

Smooth Streaming makes live responses easier to follow by fading in newly streamed words. It applies to the latest assistant message while a response is generating, including expanded reasoning text. For Agents with [parent activity phases](/docs/features/agents#activity-groups), it also lets a newly generated phase summary fold the visible activities into its collapsed group.

The setting is enabled by default. To change it, open **Settings → Chat → Messages** and toggle **Smooth streaming text (fade in new words)**.

Smooth Streaming is visual only. It does not delay token delivery, change response content, or alter saved messages. Previously loaded text and activity phases are treated as existing content when a stream reconnects or a conversation changes, so they do not replay their entrance animations. The word fade does not apply to structured regions such as code, math, artifacts, citations, and MCP UI content; parent activity phases use their own fold transition.

LibreChat automatically disables the animation when the device reports the `prefers-reduced-motion` accessibility preference, regardless of the saved toggle.

## Elapsed Generation Time

While the latest assistant response is streaming, LibreChat shows its elapsed generation time beneath the response in compact seconds or minutes. The timer begins when the message is submitted and keeps that start time during same-session navigation. If a live stream is recovered after a full reload without its original client-side start time, timing begins when the response remounts.

The timer is separate from the Smooth Streaming setting and remains available when the fade animation is disabled. It appears only on the latest response while generation is active and is not saved as message content.


# Personal Settings (https://www.librechat.ai/docs/features/settings)

Open your account menu and choose **Settings** to manage LibreChat's personal preferences and data controls. Use **Search settings** to find a control by its label or related keywords; selecting a result opens the matching tab and section. The settings layout adapts to mobile screens without hiding the tab navigation.

## General

**Display chat title in tab** uses the current conversation title as the browser-tab title. It is enabled by default and stored in the current browser. Turn it off to keep the deployment's application title in every tab. New chats continue using the application title until they receive a real conversation title.

**Clock Format** controls whether times use the system convention, a 12-hour clock, or a 24-hour clock. **System** reads the current browser's regional locale rather than the selected LibreChat translation. The choice applies to message timestamps, Scheduled Chat controls and summaries, key-expiry and balance-refill dates, Agent and prompt version dates, memories, and project chat lists.

**Week Starts On** controls whether calendar-style weekday lists follow the system convention, Sunday, or Monday. The system convention can resolve to another regional first day, such as Saturday. This changes the order of weekday controls and summaries for Scheduled Chats without changing which days a schedule runs.

Both regional preferences are stored in the current browser, so different devices can follow their own locale or explicit override.

**Show chat beside the sidebar on mobile**, under **Layout**, is off by default. When enabled, the mobile drawer stops at 80% of the viewport and leaves a strip of the current conversation visible. Tapping that strip closes the drawer. The preference is stored in the current browser; see [Mobile Drawer](/docs/features/navigation#mobile-drawer).

**Archived chats** opens the archived-conversation manager. Newly archived chats are ordered by archive time; older records without an archive timestamp fall back to their creation date. Archiving or restoring a chat does not count as new conversation activity. See [Sidebar and Navigation](/docs/features/navigation#archived-conversations).

## Chat

**Auto-Scroll to latest message on chat open** moves a newly opened conversation to its newest rendered message. Turn it off to preserve the current scroll position when returning to a chat. LibreChat waits for the target conversation rows to render before landing, including when navigation and message loading finish at different times.

**Resize images before upload** lets a user downscale large JPEG, PNG, and WebP files in the browser before sending them. The preference defaults to off and is stored in the current browser.

Administrators control whether this remains a personal choice. If [`fileConfig.clientImageResize.enabled`](/docs/configuration/librechat_yaml/object_structure/config#clientimageresize) is explicitly set to `true` or `false`, LibreChat enforces that value and locks the user toggle. If the field is omitted, the user's setting applies.

**Paste long text as a file** is enabled by default and stored in the current browser. When Upload as Text context uploads are available, pasting more than 2,500 characters in the normal message composer attaches the text as `pasted-text.txt` instead of filling the input. Shorter pastes and pastes made without the `context` capability remain inline. See [Upload Files as Text](/docs/features/upload_as_text#paste-long-text-as-a-file).

**Collapse long user messages** is off by default. When enabled, user messages taller than 256 pixels open as a compact preview with **Show more** and **Show less** controls. The collapse is visual only: the complete message remains available to copying, browser search, and assistive technology.

**Parsing LaTeX in messages** is enabled by default. The toggle controls the ambiguous single-dollar `$...$` inline-math form, which follows boundary rules that keep currency-like text such as `$50` literal. Unambiguous `$$...$$`, `\(...\)`, and `\[...\]` math remains supported when the toggle is off. Inline code, fenced code, and automatic links are not interpreted as single-dollar math.

## Keyboard Shortcuts

Open **Keyboard Shortcuts** from the account menu to review or customize bindings. The **Keyboard Shortcuts** switch is enabled by default. Turning it off disables every shortcut and removes shortcut hints from the interface while preserving custom bindings. Open the same dialog from the account menu to turn shortcuts back on.

Shortcut enablement and custom bindings are stored in the current browser. Binding rows remain visible while shortcuts are off, but editing and reset controls stay disabled until shortcuts are enabled again.

## Data Controls

The **Data Controls** tab groups personal data-management actions in one place:

- **Manage files** opens the uploaded-file manager.
- **Import conversations** imports a supported conversation export.
- **Shared chats** lists and manages published conversation links.
- **Provider API keys** manages keys supplied for configured providers.
- **Revoke all provider API keys** removes every user-provided provider key.
- **Default stateful workspace**, under **Code execution**, selects the workspace scope proposed for newly created Agents. It appears only when Stateful Code Sessions are available; changing it does not enable stateful sessions or alter existing Agents.
- **Delete TTS cache storage** removes text-to-speech audio cached in the browser; it is unavailable when the cache is empty.
- **Archive all chats** moves active conversations into **Archived chats** after confirmation without deleting them.
- **Delete all chats** permanently removes the user's conversations after confirmation.

Data deletion and key-revocation actions are server-backed and can be irreversible. Browser-only preferences and caches apply to the current browser profile rather than every device.


# Sidebar and Navigation (https://www.librechat.ai/docs/features/navigation)

LibreChat's sidebar provides conversation history, pinned chats, projects, and any additional panels enabled for the deployment. The available panel list can include chats, bookmarks, prompts, memories, and Skills.

## Desktop Sidebar

Use the sidebar toggle to open or collapse the navigation. Select a panel from the navigation rail, use conversation search when it is configured, or choose **New chat** to start a conversation. Projects have their own sidebar section and scoped new-chat shortcuts; see [Projects](/docs/features/projects).

Pinned conversations have their own paginated section and load independently from the regular history list. Pinning or unpinning a chat changes its pinned state without treating that operation as new conversation activity.

## Mobile Drawer

On mobile, the sidebar opens as a full-screen drawer by default. Its header contains the same sidebar toggle used in chat, the current panel name, a labeled panel switcher, and account controls. Search appears in the bottom bar while the conversation-history panel is active, beside a thumb-accessible **New chat** action.

The browser-only **Settings > General > Layout > Show chat beside the sidebar on mobile** preference is off by default. Turning it on limits the drawer to 80% of the viewport, leaving a dimmed strip of the current conversation visible. Tap the strip to close the drawer and return to the conversation.

You can also navigate with touch gestures:

- Swipe right across the chat pane to open the drawer.
- Swipe left inside the open drawer to close it.

The drawer follows the gesture and settles open or closed based on distance and speed. With the default full-width drawer, button-, keyboard-, and conversation-driven closes reveal the already-positioned chat without moving newly loaded content. When the chat strip is enabled, the drawer and visible conversation move together. Every close path restores focus to the header opener or chat pane after the moving surface is interactive again. Vertical scrolling, text fields, text selection, and horizontally scrollable content such as code blocks keep control of their own gestures. Reduced-motion preferences remove the drawer and strip transitions.

## Archived Conversations

Open **Archived chats** from **Settings > General** to browse or restore archived conversations. Newly archived chats are ordered by when they were archived. Conversations archived before LibreChat recorded that timestamp fall back to their creation date.

To clear the active chat list without deleting its conversations, use **Archive all chats** under **Settings > Data Controls > Your data** and confirm the action.

Archiving, unarchiving, pinning, and unpinning no longer update a conversation's chat-activity time, so history and project activity remain based on actual conversation changes.


# Message Actions (https://www.librechat.ai/docs/features/message_actions)

# Message Actions

Message actions appear below a conversation turn when they are available. User turns use right-aligned bubbles, while assistant turns keep a full-width response layout. Actions that cannot apply to a response while it is still streaming are hidden until the response settles. On pointer devices, message metadata and controls reveal on hover or keyboard focus rather than requiring a click.

## Edit and Rerun

Choose **Edit** to change a user message or an editable assistant response. The editor offers two ways to apply the change:

- **Save** updates the existing message without generating another response.
- **Update & rerun** creates a new sibling response from the edited message.

Structured assistant responses use one editor for all editable text and reasoning parts. Tool calls, errors, artifacts, and text attached to tool calls remain visible but read-only. Empty edited parts cannot be saved.

If more than one assistant part changes, save the edits without rerunning; one rerun can carry one edited assistant part. Multi-part saves are applied in order, and any parts saved before an error remain reflected in the conversation.

Rerunning an edited user message preserves its files, selected manual Skills, and quoted context. Rerunning an edited assistant response replays the parent user turn with its selected manual Skills and quotes.

While editing, use `Ctrl`/`Cmd` + `S` to save, `Ctrl`/`Cmd` + `Enter` to update and rerun, or `Escape` to cancel.

## Copy and Fork

**Copy** writes the message text to the clipboard and converts supported citations into readable references. For structured assistant responses, LibreChat joins only the user-visible text parts in order; reasoning, tool calls, errors, artifacts, and text attached to tool calls are omitted. The action is unavailable for a response that is still streaming or has no copyable text, so it does not replace clipboard contents with a partial or empty value.

Use **Fork** to branch a conversation from a selected message. See [Forking Conversations](/docs/features/fork) for details. Endpoint and message state determine whether edit, rerun, continue, feedback, read-aloud, and fork actions are available.

## Quote Excerpts

Select text in a message and choose **Add to chat** to stage it as quoted context. You can attach up to 10 excerpts to one message, with up to 1,500 characters per excerpt. LibreChat sends the excerpts as Markdown blockquotes alongside the message text and any files.

During an active Agent run, quoted excerpts follow **Steer**, **Queue**, and **Interrupt & steer** messages and remain associated with the pending action across reconnects. On a current server, an ordinary queued follow-up also keeps its quoted excerpts through app restarts and replica handoffs. If an older replica cannot accept the quoted context during a rolling deployment, LibreChat returns it to the composer instead of silently dropping it. Manually selected Skills remain staged for the next full turn rather than being added to a mid-run steer. See [Steering and Queued Messages](/docs/features/agents#steering-and-queued-messages).

## Code Block Actions

Code blocks provide actions to copy their contents, run supported code through [Code Interpreter](/docs/features/code_interpreter), or download the block as a local file. Download names use the fenced language when possible, such as `code.py` or `code.tsx`; an unknown safe language hint becomes its extension, and an absent or invalid hint falls back to `code.txt`. Error blocks do not show the download action.

## Long Pasted Text

When the browser-local **Paste long text as a file** setting is enabled and [Upload as Text context uploads](/docs/features/upload_as_text#paste-long-text-as-a-file) are available, clipboard pastes longer than 2,500 characters become a `pasted-text.txt` attachment instead of filling the composer. Otherwise, the paste remains inline. Select the attachment before sending to edit the text in a larger dialog or choose **Move back into message** to return it to the composer.

LibreChat preserves pasted-text drafts and message provenance across normal draft restoration. Edits keep the original attachment until the replacement is ready, and starting an explicit **New Chat** clears the pasted text with the rest of the draft.

## Attachment-Only Turns

You can send one or more attached files without typing placeholder text. Attachment-only turns remain valid when editing and rerunning a message, and LibreChat uses attachment filenames when it needs title context.

An empty draft with no files is still rejected. Files also do not stand in for text while answering an Agent's paused **Ask User** form, because that response path accepts answer values rather than message attachments.


# Projects (https://www.librechat.ai/docs/features/projects)

Projects let each user organize related conversations into named workspaces. They are useful for long-running workstreams, teams, clients, classes, or any topic where you want a focused set of chats without relying on search alone.

## What Projects Do

- Group conversations in a named workspace with an optional description
- Start a new chat already scoped to a project
- Move an existing conversation between projects or remove its project assignment
- Browse, sort, move, remove, or delete chats from the project workspace
- Search projects by name or description and sort by latest activity, creation date, or name
- Edit or delete projects from the dashboard, workspace, or sidebar

Projects are personal to the user who creates them. Other users do not see your project list.

## Create a Project

Open **Projects** from the sidebar and choose **New project**. Add a name and, optionally, a description. Project names can contain up to 100 characters and descriptions up to 1,000 characters.

The Projects dashboard displays each project as a folder card with its description and chat count. Use the dashboard toolbar to search names and descriptions or sort the list by latest chat activity, creation date, or name.

## Work in a Project

Open a project to see its description and assigned chats. The workspace toolbar can sort chats by last update or creation date. Use **New chat in project** to open a scoped composer; its project chip confirms that the new conversation will be assigned to that project.

Each project chat has an overflow menu with these actions:

- **Change project** opens a searchable project picker
- **Remove from project** keeps the conversation but clears its project assignment
- **Delete** deletes the conversation after confirmation

## Move Existing Chats

Open a conversation's menu and choose **Change project**. Search for and select another project to assign the chat. Choose **Remove from project** when the conversation should remain in history without a project.

The sidebar includes **All projects**, project rows, and shortcuts for starting a new chat in a project. The regular conversation history remains available outside project workspaces.

## Manage Projects

Use a project's menu from the dashboard, workspace, or sidebar to edit its name and description or delete it.

Deleting a project removes the project assignment from its chats. The conversations themselves are not deleted.

## Notes

- New chats started from a project keep that assignment when the conversation is created.
- Project searches are case-insensitive and match both names and descriptions.
- If a project is deleted while you are viewing it, LibreChat returns you to the projects list.


# Forking Chats (https://www.librechat.ai/docs/features/fork)

Forking creates a new conversation that branches off from a specific message in an existing one. The new chat copies the messages you choose, so you can explore an alternate direction, test a different prompt, or split a long thread into focused topics without altering the original.

## How to Fork a Conversation

<Steps>
<Step>

**Hover over a message** and open its menu, then select the fork icon. The message you open the menu from becomes the target message for the fork.

</Step>
<Step>

**Choose a fork option** to control which messages are copied (see below).

</Step>
<Step>

**Confirm the fork.** LibreChat opens the new conversation with the copied messages, leaving the original untouched.

</Step>
</Steps>

The *target message* is the message you opened the menu from. If you enable **Start fork here**, the target instead becomes the latest message in the conversation, and forking runs from the selected message forward.

## Fork Options

Each option copies a different set of messages relative to the target.

### Visible messages only

Copies only the visible messages: the direct path to the target message, excluding any branches.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/873bdba1-de1f-4b84-a996-b2dbfc866d55"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/0ed6ea88-5840-4dda-8f8b-305a4c34a050"
  alt="Visible messages only fork option"
/>

### Include related branches

Copies the direct path to the target message along with any branches that sit along that path.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/e633f701-acf5-4878-bdd1-29abacb3e3e7"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/0c297451-990b-4ab2-9ff2-bc3958ab7129"
  alt="Include related branches fork option"
/>

### Include all to/from here

The default option. Copies every message leading up to the target, including neighboring branches, whether or not they are visible or on the same path.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/d19b427b-e018-41e6-ab1a-6306a94be26b"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/ae3e5086-7b8f-417f-8b6e-073776536a49"
  alt="Include all to/from here fork option"
/>

## Additional Settings

**Start fork here** forks from the selected message to the latest message in the conversation, using the option chosen above.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/801e50e4-749a-42f3-83bd-a3fc06c6e189"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/bb2f2e39-091e-4b5b-926d-bf36c7a65079"
  alt="Start fork here setting"
/>

**Remember** saves the options you select and applies them to future forks, so you do not have to set them each time.

<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/9a9f61db-c3ec-4139-8f3a-e25557d95066"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/a567965f-881e-423b-9eec-e3004643a560"
  alt="Remember fork options setting"
/>

You can also set the default fork behavior from the settings menu.

<ThemeImage
  light="/images/fork/fork-settings-light.png"
  dark="/images/fork/fork-settings-dark.png"
  alt="Default fork behavior in the settings menu"
/>


# Shareable Links (https://www.librechat.ai/docs/features/shareable_links)

Shareable links let you publish a read-only branch of a conversation that others can open through a generated URL or QR code. Recipients see the published messages, branches, and artifacts without changing the original. Signed-in recipients can continue from the visible branch as a personal copy.

## Key Features

- **Easy sharing**: Generate a link in a couple of clicks.
- **QR codes**: Open the conversation on a phone by scanning a code.
- **Branching**: Shared links keep every branch of the conversation.
- **Artifacts**: React components, HTML previews, and Mermaid diagrams stay interactive.
- **File snapshots**: Referenced conversation files can be previewed or downloaded from the shared link without granting recipients access to the owner's live file ACL.
- **Continue as a copy**: Signed-in viewers can copy the visible branch into their own conversation history and continue chatting.
- **Recipient preferences**: Viewers can switch language and theme.
- **Stable updates**: Publish newer turns and the current file choice without changing the URL.
- **Shared badges**: Active links are marked in the conversation list and chat header.
- **Link management**: One dashboard to review or revoke every link you've created.

## Share a Conversation

You can start sharing from two places.

<Steps>
<Step>

**From the conversation menu.** Open the menu next to a conversation in the sidebar and choose the share option.

![Share option in the conversation menu](/images/shared-links/share-from-menu.png)

</Step>
<Step>

**From the share button.** Inside an active conversation, use the dedicated share button in the header.

![Share button in an active conversation](/images/shared-links/share-button.png)

</Step>
</Steps>

## Share Link Options

Creating a link opens a modal where you control how the conversation appears to recipients.

![Create share link modal](/images/shared-links/share-modal-create.png)

![Share link modal with management options](/images/shared-links/share-modal-options.png)

Once a link exists, the modal offers these actions:

- **Update link**: Republish the latest messages through the selected branch tail and the current file-sharing choice while keeping the same URL.
- **Generate QR code**: Create a QR code for mobile access.
- **Copy link to clipboard**: Copy the shareable URL.
- **Delete link**: Remove the link and revoke access for anyone who has it.

When file snapshots are enabled, users can choose whether the link includes files referenced in the published messages. The selected files are pinned to that share revision and served through share-specific routes without granting access to the owner's live file ACL. Updating the link replaces the pinned set and file-sharing choice; stable file URLs revalidate so a removed or replaced snapshot is not served from an older browser cache.

Publication is tied to the selected branch's exact persisted tail. If its newest message is still being saved, LibreChat retries once without switching to another branch. If the tail is still unavailable, or the conversation has no persisted messages yet, the modal shows a specific error so you can wait and retry. An unavailable shared-link view also offers **Retry** to refetch the same URL.

## QR Codes

Generate a QR code for any shared conversation, then scan it with a phone camera to open the link. The code points to the same shared URL.

![QR code generated for a shared link](/images/shared-links/qr-code.png)

![QR code download options](/images/shared-links/qr-code-download.png)

QR codes are handy for presentations, quick mobile access, printed handouts, and conference demos.

## Viewing Shared Conversations

When someone opens your link, they see a focused, read-only view of the published branch, including its date, messages, and artifacts.

![A shared conversation as a recipient sees it](/images/shared-links/shared-view.png)

## Continue a Shared Conversation

An authenticated viewer can select **Continue this chat** to create a personal conversation from the branch currently shown. LibreChat copies only the direct path to the active message, including eligible snapshotted file references, and opens the new conversation under the viewer's account.

Continuing never changes the shared snapshot or the owner's conversation. Public viewers must sign in before LibreChat can create their copy.

The copy request is tied to the share revision the viewer loaded. If the owner updates the same link before the viewer continues, LibreChat reloads the current revision and asks the viewer to retry instead of copying message positions from stale content.

## Branching

Shared links preserve conversation branches, so recipients can follow the different paths the discussion took. Use the branch navigation arrows to move between them.

![Branch navigation menu in a shared conversation](/images/shared-links/branching-menu.png)

![Navigating branches in a shared conversation](/images/shared-links/branching-support.png)

This is useful for showing how different prompts lead to different AI responses, or how a single problem can be approached several ways.

## Artifacts

Artifacts generated during the conversation stay fully functional in shared links.

![Interactive artifacts in a shared conversation](/images/shared-links/artifacts-support.png)

Recipients can:

- View interactive React components.
- See HTML previews.
- Examine Mermaid diagrams.
- Read the underlying code and the context it was generated in.

## Recipient Preferences

Viewers can adjust the interface to their own preferences without affecting your settings.

![Language and theme settings on a shared link](/images/shared-links/recipient-settings.png)

- **Theme**: Light, dark, or system.
- **Language**: View the interface in their preferred language.

## Managing Your Shared Links

Open the Shared Links dashboard under **Settings → Data Controls** to review every conversation you've shared.

![Shared links management dashboard](/images/shared-links/management-modal.png)

![Details and options for an individual shared link](/images/shared-links/management-details.png)

From the dashboard you can:

- **View all links**: See every conversation you've shared.
- **Search and filter**: Find a specific shared link and preview its content.
- **Revoke access**: Delete a link to stop sharing immediately.

Conversations with an active shared link also show a link badge in the sidebar, and the chat header's share control shows the same active state.

## Configuration

Three environment variables in your `.env` file control the feature:

- **`ALLOW_SHARED_LINKS`** (default: `true`): Enables the shared links feature. Set to `false` to stop users from creating links.
- **`ALLOW_SHARED_LINKS_PUBLIC`** (default: `false`): Controls whether links open without authentication. By default, recipients must be logged in. Set to `true` to allow public, unauthenticated access.
- **`SHARED_LINKS_SNAPSHOT_FILES`** (default: `true`): Controls whether shared links can include referenced conversation files. This environment variable overrides [`interface.sharedLinks.snapshotFiles`](/docs/configuration/librechat_yaml/object_structure/interface#sharedlinks) when set.

```bash title=".env"
# Enable shared links (default)
ALLOW_SHARED_LINKS=true

# Require authentication to view shared links (default)
ALLOW_SHARED_LINKS_PUBLIC=false

# Include referenced files in shared-link snapshots (default)
SHARED_LINKS_SNAPSHOT_FILES=true
```

Role permissions decide which users can create links, share them with authenticated users, or make them visible to everyone on the instance:

```yaml title="librechat.yaml"
interface:
  sharedLinks:
    create: true
    share: true
    public: false
    snapshotFiles: true
```

`sharedLinks.public` controls whether a user can toggle "share with everyone." `ALLOW_SHARED_LINKS_PUBLIC` still decides whether those public links are viewable without logging in. `sharedLinks.snapshotFiles` controls the default file-snapshot behavior from YAML, and `SHARED_LINKS_SNAPSHOT_FILES` can override or disable it globally.

<Callout type="info">
For the full environment variable reference, see the [.env Configuration](/docs/configuration/dotenv#shared-links) page.
</Callout>

## Frequently Asked Questions

<Accordions type="single" collapsible>
<Accordion title="Can recipients respond to shared conversations?">

The shared snapshot remains read-only. Signed-in recipients can select **Continue this chat** to create a personal copy and interact with the AI there.

</Accordion>
<Accordion title="What happens if I delete a conversation after sharing it?">

The link stops working. Anyone who navigates to it sees a "Shared link not found" page.

</Accordion>
<Accordion title="Can I edit a conversation after sharing it?">

Yes. Edits to messages already included in the link appear in the shared view. New turns outside the published branch tail appear only after you select **Update link**; the URL stays the same.

</Accordion>
<Accordion title="Do shared links work offline?">

No. Recipients need an internet connection to view shared conversations.

</Accordion>
</Accordions>


# Temporary Chat (https://www.librechat.ai/docs/features/temporary_chat)

Temporary chats let you ask something without keeping it. Use them for sensitive topics, quick experiments, or anything you don't need to save. A temporary chat stays out of your history sidebar, never appears in search, can't be bookmarked, and is deleted automatically once its retention period ends (30 days by default).

## Start a Temporary Chat

<Steps>

<Step>

**Open the model menu.** Select the model dropdown at the top left of the chat view.

</Step>

<Step>

**Turn on Temporary Chat.** Toggle the **Temporary Chat** switch to the ON position.

<Frame>
![Temporary Chat toggle in the model menu](https://github.com/user-attachments/assets/ebd5370d-7fac-45af-b0d3-2de59df506e9)
</Frame>

</Step>

<Step>

**Confirm it's active.** Before the first message, the landing view identifies the session as a Temporary Chat and explains that it stays out of history and is deleted automatically. After the conversation starts, a read-only indicator remains in the header even though the setup toggle is no longer available.

</Step>

</Steps>

## What a Temporary Chat Does

- Does not appear in the chat history sidebar.
- Is excluded from search results.
- Cannot be bookmarked.
- Shows a dedicated landing state before the first message and an active indicator afterward.
- Is stored in the database until its retention period ends, then deleted automatically.

<Callout type="info" title="Retention period">

The default retention period is 30 days. Administrators can change it. See [Configuration](#configuration-administrators) below.

</Callout>

## Configuration (administrators)

Temporary Chat is available to users by default. Administrators control whether the feature is offered and how long temporary chats are kept.

**Availability** is governed by the `TEMPORARY_CHAT` role permission. Manage it for each role from the [Admin Panel](/docs/features/admin_panel). The `interface.temporaryChat` option in `librechat.yaml` only seeds this permission for the default `USER` role at startup and is deprecated for permission management.

**Retention** is set with `interface.temporaryChatRetention` (in hours). The minimum is 1 hour, the maximum is 8760 (1 year), and the default is 720 (30 days).

<Tabs items={['librechat.yaml (recommended)', 'Environment variable (deprecated)']}>

<Tabs.Tab>

```yaml filename="librechat.yaml"
interface:
  temporaryChat: true
  temporaryChatRetention: 168 # retain temporary chats for 7 days
```

</Tabs.Tab>

<Tabs.Tab>

```bash filename=".env"
# Hours to retain temporary chats (default: 720 = 30 days)
TEMP_CHAT_RETENTION_HOURS=168
```

<Callout type="warning" title="Deprecated">

`TEMP_CHAT_RETENTION_HOURS` is deprecated. Prefer `interface.temporaryChatRetention` in `librechat.yaml`, which takes precedence over the environment variable.

</Callout>

</Tabs.Tab>

</Tabs>

Common retention values:

| Value | Period |
| --- | --- |
| `1` | 1 hour (minimum) |
| `24` | 1 day |
| `168` | 1 week |
| `720` | 30 days (default) |
| `8760` | 1 year (maximum) |

For the full set of options, including `retentionMode` and `retainAgentFiles`, see the [interface reference](/docs/configuration/librechat_yaml/object_structure/interface#temporarychatretention).


# Query Parameters (https://www.librechat.ai/docs/features/url_query)

LibreChat can configure a chat conversation directly from the URL. Append query parameters to a chat path to choose the endpoint and model, pre-fill the input, or override conversation settings before the chat loads.

## Chat Paths

Query parameters must follow a valid chat path:

- New conversations: `/c/new?`
- Existing conversations: `/c/[conversation-id]?` (where `conversation-id` is an existing one)

```bash
https://your-domain.com/c/new?endpoint=ollama&model=llama3%3Alatest
https://your-domain.com/c/03debefd-6a50-438a-904d-1a806f82aad4?endpoint=openAI&model=o1-mini
```

## Basic Usage

The `endpoint` and `model` parameters cover most cases. Set both for predictable results:

```bash
https://your-domain.com/c/new?endpoint=azureOpenAI&model=o1-mini
```

### Endpoint selection

Use `endpoint` on its own to switch endpoints without naming a model:

```bash
https://your-domain.com/c/new?endpoint=google
```

When only `endpoint` is set, LibreChat falls back to the last model selected for that endpoint (from `localStorage`). If there is no previous selection, it uses the first model in the endpoint's list.

The `endpoint` value must be one of:

```bash
openAI, azureOpenAI, google, anthropic, assistants, azureAssistants, bedrock, agents
```

For a [custom endpoint](/docs/quick_start/custom_endpoints), use its configured name as the value (case-insensitive):

```bash
# endpoint=perplexity for a custom endpoint named `Perplexity`
https://your-domain.com/c/new?endpoint=perplexity&model=llama-3.1-sonar-small-128k-online
```

### Model selection

Use `model` on its own to switch models within the current endpoint:

```bash
https://your-domain.com/c/new?model=gpt-4o
```

When only `model` is set, LibreChat applies it only if the model exists in the current endpoint. The current endpoint is the default endpoint or the last one selected.

### Prompt

The `prompt` parameter pre-populates the chat input:

```bash
https://your-domain.com/c/new?prompt=Explain quantum computing
```

`q` is an interchangeable shorthand for `prompt`:

```bash
https://your-domain.com/c/new?q=Explain quantum computing
```

Combine it with other parameters:

```bash
https://your-domain.com/c/new?endpoint=anthropic&model=claude-3-5-sonnet-20241022&prompt=Explain quantum computing
```

### Automatic submission

Add `submit=true` to send the prompt automatically, without manual confirmation:

```bash
https://your-domain.com/c/new?prompt=Explain quantum computing&submit=true
```

This is useful for automated workflows (Raycast, Alfred, Automator) and external integrations. Combine it with the other parameters for a fully scripted launch:

```bash
https://your-domain.com/c/new?endpoint=openAI&model=gpt-4&prompt=Explain quantum computing&submit=true
```

## URL Encoding

Special characters in query values must be URL-encoded. Common substitutions:

| Character | Encoded |
| --------- | ------- |
| `:`       | `%3A`   |
| `/`       | `%2F`   |
| `?`       | `%3F`   |
| `#`       | `%23`   |
| `&`       | `%26`   |
| `=`       | `%3D`   |
| `+`       | `%2B`   |
| Space     | `%20` (or `+`) |

For example:

```ts
Original: `Write a function: def hello()`
Encoded: `/c/new?prompt=Write%20a%20function%3A%20def%20hello()`
```

JavaScript's built-in `encodeURIComponent()` handles the encoding for you:

```javascript
const prompt = "Write a function: def hello()";
const encodedPrompt = encodeURIComponent(prompt);
const url = `/c/new?prompt=${encodedPrompt}`;
console.log(url);
```

Run this in your browser console (`Ctrl+Shift+I`) to see the encoded URL.

## Specs, Agents, and Assistants

### Model specs

Select a [model spec](/docs/configuration/librechat_yaml/object_structure/model_specs) by name:

```bash
https://your-domain.com/c/new?spec=meeting-notes-gpt4
```

This loads every setting defined by the spec. Other model parameters in the URL are ignored when `spec` is present.

### Agents

Load an agent by ID without naming an endpoint:

```bash
https://your-domain.com/c/new?agent_id=your-agent-id
```

This sets the endpoint to `agents` automatically.

### Assistants

Load an assistant by ID the same way:

```bash
https://your-domain.com/c/new?assistant_id=your-assistant-id
```

This sets the endpoint to `assistants` automatically.

## Supported Parameters

### LibreChat settings

| Parameter | Description |
| --------- | ----------- |
| `maxContextTokens` | Override the system-defined context window. |
| `resendFiles` | Control file resubmission in subsequent messages. |
| `promptPrefix` | Set custom instructions / system message. |
| `imageDetail` | Image quality: `low`, `auto`, or `high`. Applies only to OpenAI, OpenAI-like custom endpoints, and Azure OpenAI (defaults to `auto`). |
| `spec` | Select a [model spec](/docs/configuration/librechat_yaml/object_structure/model_specs) by exact name. When set, other model parameters are ignored in favor of the spec. If specs are configured with `enforce: true`, this parameter may be required for URL query params to work. |
| `fileTokenLimit` | Maximum token limit for file processing, to control cost and resource usage. The request value overrides the YAML default. |

### Model parameters

Supported model parameters vary by endpoint. Values must be valid according to the provider's API.

**OpenAI, Custom, Azure OpenAI:**

```bash
temperature, presence_penalty, frequency_penalty, stop, top_p, max_tokens,
reasoning_effort, reasoning_summary, verbosity, useResponsesApi, web_search, disableStreaming
```

**Google, Anthropic:**

```bash
topP, topK, maxOutputTokens, thinking, thinkingBudget, thinkingLevel, web_search, url_context
```

For Google endpoints, set `url_context=true` to let supported Gemini text models read URLs included in the user message. YouTube URLs are handled with native video understanding when URL Context is enabled.

**Anthropic, Bedrock (Anthropic models), OpenRouter custom endpoints:**

Set `promptCache` to `true` or `false` to toggle prompt caching. Set `promptCacheTtl` to `5m` or `1h` to choose the cache lifetime when prompt caching is enabled:

```bash
promptCache
promptCacheTtl=1h
```

See the [Anthropic prompt caching docs](https://www.anthropic.com/news/prompt-caching) and the [Bedrock prompt caching docs](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-get-started) for details.

**Bedrock:**

```bash
# Bedrock region
region=us-west-2
# Bedrock equivalent of `max_tokens`
maxTokens=200
# Bedrock reasoning effort (for supported models like ZAI, MoonshotAI)
reasoning_effort=medium
```

**Assistants / Azure Assistants:**

```bash
# Overrides existing assistant instructions for the current run
instructions=your+instructions
```

```bash
# Adds the current date and time to `additional_instructions` for each run
append_current_datetime=true
```

Most of these parameters are shared with [Model Spec Preset Fields](/docs/configuration/librechat_yaml/object_structure/model_specs); refer there for the full reference.

### Examples

Multiple parameters in one URL:

```bash
https://your-domain.com/c/new?endpoint=google&model=gemini-2.0-flash-exp&temperature=0.7&prompt=Oh hi mark
```

Loading a model spec with a prompt:

```bash
https://your-domain.com/c/new?spec=meeting-notes-gpt4&prompt=Here%20is%20the%20transcript...
```

When using `spec`, other model parameters are ignored in favor of the spec's configuration.

## Validation

All parameters are validated against LibreChat's schema before they are applied. Invalid parameters and values are ignored; valid settings are applied to the conversation.

<Callout type="warning" title="Use query parameters carefully">
- Misuse or exceeding provider limits can produce API errors.
- If you hit a bad request error, reset the conversation by clicking **New Chat**.
- A parameter has no effect if the selected endpoint does not support it.
</Callout>

## Best Practices

1. Set both `endpoint` and `model` whenever possible.
2. Confirm the endpoint supports each parameter you pass.
3. Keep values within the provider's limits.
4. Test parameter combinations before sharing URLs.

Query parameters make it easy to share specific conversation configurations, bookmark different chat setups, and automate chat startup from external tools.


# Import Conversations (https://www.librechat.ai/docs/features/import_convos)

Conversations Import lets you bring conversations exported from other AI chat applications into LibreChat. Supported sources are [ChatGPT](https://chatgpt.com/), [Claude](https://claude.ai/), and [ChatbotUI v1](https://github.com/mckaywrigley/chatbot-ui/tree/b865b0555f53957e96727bc0bbb369c9eaecd83b?tab=readme-ov-file#legacy-code).

<Callout type="info" title="Where to find it">
Import lives under **Settings** → **Data Controls** in LibreChat.
</Callout>

## Export from the source application

First, export your data from the application you are migrating away from. The steps differ by source.

<Callout type="info" title="Looking to export out of LibreChat instead?">
See [Export from LibreChat](#export-from-librechat) at the bottom of this page.
</Callout>

<Tabs items={["ChatGPT", "Claude", "ChatbotUI v1"]}>
<Tabs.Tab>

<Steps>
<Step>

**Request your export.** Follow the [ChatGPT export instructions](https://help.openai.com/en/articles/7260999-how-do-i-export-my-chatgpt-history-and-data) to export your conversations.

</Step>
<Step>

**Download the archive.** You will receive an email with a download link. The archive is a zip file with a random name such as `d119d98bb3711aff7a2c73bcc7ea53d96c984650d8f7e033faef78386a9907-2024-01-01-10-30-00.zip`.

</Step>
<Step>

**Extract the archive** to access the `conversations.json` file inside.

</Step>
</Steps>

</Tabs.Tab>
<Tabs.Tab>

<Steps>
<Step>

**Request your export.** Follow the [Claude export instructions](https://support.claude.com/en/articles/9450526-how-can-i-export-my-claude-data) to export your conversations.

</Step>
<Step>

**Download the archive.** You will receive an email with a download link for your export archive.

</Step>
<Step>

**Extract the archive** to access the `conversations.json` file inside.

</Step>
</Steps>

</Tabs.Tab>
<Tabs.Tab>

Export your conversations from [ChatbotUI v1](https://github.com/mckaywrigley/chatbot-ui/tree/b865b0555f53957e96727bc0bbb369c9eaecd83b?tab=readme-ov-file#legacy-code), then import the resulting JSON file directly. No extraction step is required.

</Tabs.Tab>
</Tabs>

## Import into LibreChat

<Steps>
<Step>

**Open Data Controls.** In LibreChat, go to **Settings** → **Data Controls**.

</Step>
<Step>

**Select your file.** Click **Import** and choose the `conversations.json` file from your extracted archive (or the exported JSON file for ChatbotUI v1).

</Step>
<Step>

**Wait for confirmation.** A notification appears once the import finishes.

</Step>
</Steps>

## Export from LibreChat

Export is per conversation, and lives in the **Export and Share** menu in the chat header (under the overflow menu on mobile). Open the conversation you want, choose **Export**, pick a format, and confirm.

<OptionTable
  options={[
    ['markdown', '.md', 'Readable transcript. The default.', ''],
    ['text', '.txt', 'Plain-text transcript.', ''],
    ['json', '.json', 'Structured export that preserves the full message data.', 'Supports exporting all message branches'],
    ['csv', '.csv', 'Tabular export, one row per message.', 'Supports exporting all message branches'],
    ['screenshot', '.png', 'Image of the rendered conversation.', ''],
  ]}
/>

Two options in the dialog depend on the format you pick:

- **Export all message branches** applies to `json` and `csv` only. Other formats export just the visible branch.
- The remaining export options are unavailable for `csv` and `screenshot`.

<Callout type="info" title="Scope">

There is no built-in bulk export of every conversation at once, and a brand-new or search-results conversation has nothing to export, so the menu does not appear for those.

</Callout>


# Authentication (https://www.librechat.ai/docs/features/authentication)

 

LibreChat has a user authentication system that allows users to sign up and log in securely and easily. The system is scalable and can handle a large number of concurrent users without compromising performance or security.

By default, we have email signup and login enabled, which means users can create an account using their email address and a password. They can also reset their password if they forget it.

Additionally, our system can integrate social logins from various platforms such as Google, GitHub, Discord, OpenID, and more. This means users can log in using their existing accounts on these platforms, without having to create a new account or remember another password.

**For further details, refer to the configuration guides provided here: [Authentication](/docs/configuration/authentication)**

<Callout type="warning" title="Important">
- When you run an unscoped single-tenant deployment for the first time, create an account by clicking **Sign up** on the login page. That first account becomes the admin and holds all administrative capabilities on the instance. Tenant-scoped deployments do not auto-promote their first registered user; provision tenant administrators through a trusted administrative flow. See [Access Control](/docs/features/access_control) for the full authorization model covering users, groups, roles, resource ACLs, and system-level admin grants.
- For an unscoped single-tenant deployment, the first account should ideally be a local account (email and password).
</Callout>

**See also:** [Access Control](/docs/features/access_control), LibreChat's granular permission system for users, groups, and roles, covering per-resource sharing of agents, prompts, MCP servers, and feature-level permissions.

## Staying Signed In

Using LibreChat requires an account. There is no anonymous or guest mode for chatting, so you cannot start a conversation without logging in. The one exception is viewing: when an admin sets `ALLOW_SHARED_LINKS_PUBLIC=true`, anyone holding a [shared link](/docs/features/shareable_links) can read that conversation without an account. They can only read it.

Two settings decide how long a session lasts, and both are configurable:

- `SESSION_EXPIRY`: how long an access token stays valid. Defaults to **15 minutes**.
- `REFRESH_TOKEN_EXPIRY`: how long you stay signed in overall. Defaults to **7 days**.

The short access token is renewed automatically in the background while you are using LibreChat, so the 15-minute figure is not how often you are asked to log in again. Being signed out usually means the refresh token reached the end of its window, or the browser dropped the refresh cookie.

Each renewal does hand back a new refresh token, but it is signed against the same session and inherits that session's original expiry. `REFRESH_TOKEN_EXPIRY` is therefore measured from when you logged in, not from your last activity: staying active does not extend it, and you are signed out when the window runs out.

If you are being logged out sooner than expected, raise `REFRESH_TOKEN_EXPIRY`. Both variables are documented in the [.env reference](/docs/configuration/dotenv).

<Callout type="info" title="OpenID token reuse changes who owns the session">

Everything above describes refresh tokens that LibreChat issues itself. With [`OPENID_REUSE_TOKENS=true`](/docs/configuration/authentication/OAuth2-OIDC/token-reuse), the cookie holds your OpenID provider's refresh token instead, so that provider's lifetime, rotation, and revocation policy decide when the session ends. `REFRESH_TOKEN_EXPIRY` does not extend an IdP credential that has expired or been revoked; change the session policy at the provider.

</Callout>


<ThemeImage
  light="https://github.com/danny-avila/LibreChat/assets/32828263/786fa525-73c4-4640-b4cf-91925ad8802e"
  dark="https://github.com/danny-avila/LibreChat/assets/32828263/dddc34c6-9602-4177-89e8-4c0db01b0eac"
  alt="Social login buttons on the LibreChat sign-in screen"
/>


# Access Control (https://www.librechat.ai/docs/features/access_control)

# Granular Access Control

LibreChat ships with a full authorization system on top of authentication. Access is not "all-or-nothing": every shareable entity in the app (agents, prompts, MCP servers, remote agents, files, conversations) has its own Access Control List (ACL), and every feature can be independently enabled or restricted per **user**, **group**, **role**, or **publicly**.

This page explains how the pieces fit together so you can model permissions to match your organization, from a small team where everyone shares freely, to an enterprise deployment with sync'd Entra ID groups, custom roles, and delegated admins.

<Callout type="info" title="Admin Panel">
  A dedicated [**LibreChat Admin Panel**](/docs/features/admin_panel) is the upcoming UI for
  managing users, groups, roles, custom permission profiles, and system-wide grants introduced in
  [v0.8.5](/changelog/v0.8.5). This page documents the underlying model, which is available today in
  LibreChat itself.
</Callout>

## The Access Model at a Glance

LibreChat's authorization has three independent layers that compose together:

| Layer                   | Scope                          | What it controls                                                                                                                                                                                        |
| ----------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Feature Permissions** | Per role (USER, ADMIN, custom) | Whether a principal can _use_, _create_, _share_, or _share publicly_ a class of feature (agents, prompts, MCP servers, memories, web search, etc.). Configured in `librechat.yaml` or the admin panel. |
| **Resource ACLs**       | Per individual resource        | Who can view, edit, delete, or re-share a specific agent, prompt, MCP server, etc. Managed by the resource owner via the in-app share dialog.                                                           |
| **System Grants**       | Platform-wide                  | Admin capabilities (e.g. `manage:users`, `manage:roles`, `read:usage`). Used by the admin panel.                                                                                                        |

All three are evaluated for the same four principal types:

- **User**: an individual LibreChat account
- **Group**: a collection of users (local or synced from Entra ID)
- **Role**: a named permission profile (e.g. `USER`, `ADMIN`, or any custom role)
- **Public**: every authenticated user on the instance

## Layer 1: Feature Permissions (Role-Based)

Feature-level permissions gate entire capabilities of the app for a given role. They answer questions like _"Can users in this role create agents at all?"_, _"Are they allowed to share prompts publicly?"_, _"Can they invoke the code interpreter?"_.

### Built-in System Roles

LibreChat ships with two system roles that are always present and cannot be deleted:

- **`ADMIN`**: assigned to the first account registered in an unscoped single-tenant deployment. Tenant-scoped deployments must provision administrators through a trusted administrative flow. Admins can see every resource, modify any setting, access the admin panel, and configure platform-wide behavior.
- **`USER`**: the default role assigned to every new account.

Admins can be promoted manually by updating the user document in MongoDB, see [Administrator Controls](/docs/features/agents#administrator-controls).

### Permission Types

Each role holds a matrix of **permission types × actions**:

| Permission Type  | Available actions                                         |
| ---------------- | --------------------------------------------------------- |
| `AGENTS`         | `USE`, `CREATE`, `SHARE`, `SHARE_PUBLIC`                  |
| `PROMPTS`        | `USE`, `CREATE`, `SHARE`, `SHARE_PUBLIC`                  |
| `MCP_SERVERS`    | `USE`, `CREATE`, `SHARE`, `SHARE_PUBLIC`, `CONFIGURE_OBO` |
| `REMOTE_AGENTS`  | `USE`, `CREATE`, `SHARE`, `SHARE_PUBLIC`                  |
| `SKILLS`         | `USE`, `CREATE`, `SHARE`, `SHARE_PUBLIC`                  |
| `SHARED_LINKS`   | `CREATE`, `SHARE`, `SHARE_PUBLIC`                         |
| `SCHEDULES`      | `USE`, `CREATE`                                            |
| `MEMORIES`       | `USE`, `CREATE`, `UPDATE`, `READ`, `OPT_OUT`              |
| `BOOKMARKS`      | `USE`                                                     |
| `MULTI_CONVO`    | `USE`                                                     |
| `TEMPORARY_CHAT` | `USE`                                                     |
| `RUN_CODE`       | `USE`                                                     |
| `WEB_SEARCH`     | `USE`                                                     |
| `FILE_SEARCH`    | `USE`                                                     |
| `FILE_CITATIONS` | `USE`                                                     |
| `MARKETPLACE`    | `USE`                                                     |
| `PEOPLE_PICKER`  | `VIEW_USERS`, `VIEW_GROUPS`, `VIEW_ROLES`                 |

The distinction between `SHARE` and `SHARE_PUBLIC` is important: you can allow a role to share agents with _specific_ users or groups (`SHARE`) without letting them make agents visible to _everyone_ on the instance (`SHARE_PUBLIC`).

### Configuring Feature Permissions

The recommended way to manage feature permissions is the [**LibreChat Admin Panel**](/docs/features/admin_panel), which edits the permission matrix directly on each role (including any custom roles you create). Changes take effect without redeploying LibreChat and are scoped to the exact role you want to modify, rather than the global `USER` default.

<Callout type="warning" title="Legacy: `librechat.yaml` interface block">
  The [`interface` block](/docs/configuration/librechat_yaml/object_structure/interface) in
  `librechat.yaml` can still seed permissions for the default `USER` role at startup, and remains
  useful for bootstrapping a fresh instance or for fully file-driven deployments. However, it only
  targets the `USER` role and cannot express differences across custom roles. For ongoing permission
  management, prefer the admin panel.
</Callout>

### Custom Roles

Beyond `USER` and `ADMIN`, administrators can create **custom roles** with their own feature-permission matrix (introduced in v0.8.5; see [#12528](https://github.com/danny-avila/LibreChat/pull/12528)). A user can hold multiple roles, and their effective permissions are the union across all held roles. Custom roles are managed from the admin panel.

### Role- and Group-Scoped Configuration Overrides

In addition to feature flags, v0.8.5 introduced a **DB-backed configuration override** system ([#12354](https://github.com/danny-avila/LibreChat/pull/12354)). This lets you assign a _different `librechat.yaml`-style config_ to specific groups or roles. For example, a "Research" group might have access to additional endpoints, a higher recursion limit, and different agent capabilities than the default. Overrides are resolved at login and composed on top of the base configuration.

## Layer 2: Resource ACLs (Per-Entity Sharing)

Every shareable resource in LibreChat has its own Access Control List, independent of role-based permissions. This is how an individual user with `SHARE` permission chooses _who_ gets access to _their_ agent, prompt, or MCP server.

### Resource Types

Resource ACLs currently apply to:

- **Agents** (`agent`)
- **Prompts / Prompt Groups** (`promptGroup`)
- **MCP Servers** (`mcpServer`)
- **Remote Agents** (`remoteAgent`), for the [Agents API](/docs/features/agents_api)
- **Files** (`file`), typically inherited from the resource that uses them
- **Projects** (`project`), supports inheritance so resources shared to a project automatically inherit ACLs

### Access Roles (Permission Presets)

Rather than exposing raw permission bits to end users, sharing uses three named roles per resource type:

| Role       | Permission bits                                 | What the grantee can do                                             |
| ---------- | ----------------------------------------------- | ------------------------------------------------------------------- |
| **Viewer** | `VIEW` (`0b0001`)                               | Use / interact with the resource                                    |
| **Editor** | `VIEW` + `EDIT` (`0b0011`)                      | View and modify the resource's settings, instructions, tools, files |
| **Owner**  | `VIEW` + `EDIT` + `DELETE` + `SHARE` (`0b1111`) | Full control: edit, delete, and re-share to others                  |

Under the hood, permissions are stored as a bitmask (`permBits`) against each (resource, principal) pair; supersets are handled automatically, so granting Editor implies Viewer.

### Granting Access from the UI

1. Open the resource (agent builder, prompt form, MCP server settings, etc.)
2. Click the **Share** button (visible when you are the owner, an admin, or have been granted `SHARE`)
3. In the share dialog:
   - Use the people picker to search for **users**, **groups**, or **roles** to add
   - Pick an access role (Viewer / Editor / Owner) per principal
   - Optionally toggle **Public access** to make the resource visible to everyone on the instance (requires the `SHARE_PUBLIC` feature permission)
4. Save. Grantees see the resource the next time they refresh.

<Callout type="warning" title="Guarding Against Data Leaks">
  Editor and Owner grantees can see everything configured on the resource, including system
  instructions, attached files, and tools. Any agent may also leak attached data through
  conversation output, so make sure your instructions are robust against prompt injection before
  granting edit access or making an agent public.
</Callout>

### What Grantees See

- **Viewers** see the resource as a ready-to-use item in the relevant picker (e.g. the agent dropdown). They cannot open the builder, see raw instructions, or modify settings.
- **Editors** can open the resource's configuration and modify it, but cannot delete it or re-share it.
- **Owners** have the same UI as the original author, and can delete and re-share freely.
- **The original author** always retains full control regardless of ACL state, and admins can manage any resource on the instance.

### Project Inheritance

Permissions can be inherited from a parent **project**. When an ACL entry is inherited, the `inheritedFrom` link points back to the source. This is what powers the "Global" project in LibreChat, where a resource added to the global project becomes available to all users without needing an entry per principal.

## Layer 3: System Grants (Admin Capabilities)

System grants are a separate grant table used for **admin-level capabilities**, answering questions like _"Can this user access the admin panel?"_ or _"Can this group manage MCP servers globally?"_. They are always scoped to a principal (user, group, or role) and a capability string.

The canonical capabilities include:

| Capability                           | Purpose                                       |
| ------------------------------------ | --------------------------------------------- |
| `access:admin`                       | Access the admin panel at all                 |
| `read:users` / `manage:users`        | View / modify user accounts                   |
| `read:groups` / `manage:groups`      | View / modify groups                          |
| `read:roles` / `manage:roles`        | View / modify custom roles                    |
| `read:configs` / `manage:configs`    | View / modify all system configuration        |
| `read:configs:<section>` / `manage:configs:<section>` | View / modify one top-level configuration section |
| `assign:configs:{user\|group\|role}` | Assign config-override profiles to principals |
| `read:usage`                         | View platform usage and telemetry             |
| `read:agents` / `manage:agents`      | View / moderate every agent on the instance   |
| `read:prompts` / `manage:prompts`    | View / moderate every prompt                  |
| `manage:mcpservers`                  | Manage MCP servers globally                   |

Manage capabilities imply their corresponding read capability (e.g. holding `manage:users` automatically grants `read:users`). This also applies to configuration sections: `manage:configs:endpoints` satisfies `read:configs:endpoints`. Users with only section-scoped config access receive admin configuration responses filtered to the sections they can read; users with no config-read grant receive `403`.

A `SystemRoles.ADMIN` user implicitly holds all capabilities; grants let you **delegate** a subset of admin powers to non-admin principals without making them full admins. Access to the admin UI itself still requires `access:admin`.

System grants are issued and revoked via the admin panel.

## Principals in Depth

### Users

Standard LibreChat accounts. Users can be **local** (email/password) or **federated** (OAuth2, OIDC, SAML, LDAP). Federated users can be matched to an external identity (`idOnTheSource`); for Entra ID this is the OID, which is what enables group sync.

### Groups

A group is a named collection of users. LibreChat supports two sources:

- **Local groups**: created and managed from the admin panel or directly in the database. Members are LibreChat user IDs.
- **Entra ID (Azure AD) groups**: synced from Microsoft Graph when a user logs in via Azure OIDC with [token reuse](/docs/configuration/authentication/OAuth2-OIDC/token-reuse) enabled. Each synced group stores its Entra Object ID as `idOnTheSource`, which keeps LibreChat in lockstep with tenant membership.

Groups can appear in any ACL, in `peoplePicker` search, and as a principal target for config overrides or system grants. A single resource shared with a 500-person group is one ACL entry (not 500), and membership changes in Entra propagate automatically on the next login.

### ACL Principal Cache Tuning

LibreChat caches the group IDs used to resolve a user's ACL principals for five minutes by default. The cache is scoped by tenant and member identity, and group membership changes invalidate affected entries. It does not cache the user's role or the lookup of `idOnTheSource`, so those values continue to be resolved from the current request user or database.

```bash filename=".env"
# Group-membership cache lifetime; 0 disables the cache
USER_PRINCIPALS_CACHE_TTL_MS=300000

# Redis-backed deployments only: deduplicate cold cache builds across replicas
USER_PRINCIPALS_LOCK_TTL_MS=5000
USER_PRINCIPALS_LOCK_WAIT_MS=5000
```

When the principal cache is Redis-backed, the lock settings prevent several replicas from rebuilding the same cold entry at once. Set `USER_PRINCIPALS_LOCK_TTL_MS=0` to disable only build locking; shared cache storage and cross-process invalidation remain active. Cache and lock failures fall back to the database and do not block permission checks.

### Roles

Any system or custom role can be used as a principal. Sharing an agent with a role (e.g. `SupportEngineers`) gives every user currently holding that role access, without needing to enumerate individuals. Roles can be hidden from the people picker via [`interface.peoplePicker.roles`](/docs/configuration/librechat_yaml/object_structure/interface#peoplepicker) for environments where role-based sharing is an admin-only concern.

### Public

A special principal that matches every authenticated user. Public grants are only permitted when the granting user holds the `SHARE_PUBLIC` feature permission for that resource type.

## People Picker Visibility

The people picker (the search box in share dialogs) can be constrained at the instance level to hide principal types that aren't relevant for your deployment:

```yaml filename="librechat.yaml"
interface:
  peoplePicker:
    users: true
    groups: true
    roles: false
```

This only affects the _search UI_; existing ACL entries for hidden principal types continue to work and are enforced normally.

## Migrations from Pre-ACL Versions

Versions prior to v0.8.0-rc3 used a simpler ownership model. Upgrading requires running the ACL migration so existing agents and prompts remain accessible:

**Dry run (preview changes):**

```bash
npm run migrate:agent-permissions:dry-run
npm run migrate:prompt-permissions:dry-run
```

**Execute:**

```bash
npm run migrate:agent-permissions
npm run migrate:prompt-permissions
```

See the [agents migration guide](/docs/features/agents#migration-required-v080-rc3) for Docker variants and batch-size options.

## Related Documentation

- [Agents: Sharing and Permissions](/docs/features/agents#sharing-and-permissions)
- [Interface Configuration (feature permissions)](/docs/configuration/librechat_yaml/object_structure/interface)
- [Authentication](/docs/features/authentication)
- [OpenID Connect Token Reuse (required for Entra ID group sync)](/docs/configuration/authentication/OAuth2-OIDC/token-reuse)
- [Azure / Entra ID OAuth2](/docs/configuration/authentication/OAuth2-OIDC/azure)
- [SharePoint Integration](/docs/configuration/sharepoint)
- [Agents API (Remote Agents)](/docs/features/agents_api)
- [LibreChat Admin Panel](/docs/features/admin_panel)


# Admin Panel (https://www.librechat.ai/docs/features/admin_panel)

# LibreChat Admin Panel

The **LibreChat Admin Panel** is a standalone browser-based management interface for LibreChat. It connects to the same database as LibreChat itself and provides a GUI for the administrative tasks that power [granular access control](/docs/features/access_control): user and group administration, role management, configuration overrides scoped to roles or groups, and system-level capability grants.

<Callout type="info" title="Status: Preview">
  The admin panel is available for testing now and is the upcoming management surface that builds on
  the admin APIs introduced in [LibreChat v0.8.5](/changelog/v0.8.5). Source, issues, and releases
  live at
  [github.com/ClickHouse/librechat-admin-panel](https://github.com/ClickHouse/librechat-admin-panel).
</Callout>

## What It Does

The admin panel is a thin client: all data lives in LibreChat's database, and every action goes through the versioned `/api/admin/*` endpoints on the LibreChat API server. It gives administrators a single place to:

- **Manage configuration**: view and edit every LibreChat setting through a dynamic, schema-driven form. New fields added to the config schema appear automatically, no admin-panel release required.
- **Apply per-principal overrides**: scope configuration overrides to specific roles or groups, with a priority-based cascade that determines the final resolved value each user sees at login.
- **Administer users**: list, search, and view every account on the instance.
- **Manage groups**: create and delete groups, add/remove members, and use groups as first-class principals in ACLs and overrides.
- **Manage roles**: create custom roles beyond the built-in `USER` / `ADMIN`, edit their feature-permission matrix, and assign users to roles.
- **Issue system grants**: delegate admin capabilities (e.g. `manage:users`, `read:usage`, `manage:mcpservers`) to specific users, groups, or roles without making them full admins.
- **Authenticate**: log in with a local LibreChat admin account, or via OpenID SSO / SAML / supported OAuth providers when those are enabled on the LibreChat instance.

For the underlying permission model (principals, resource ACLs, capabilities, and how the layers compose), see the [Access Control](/docs/features/access_control) page.

## Architecture

```
┌──────────────────┐         ┌──────────────────┐         ┌──────────────┐
│  Admin Panel     │ ───────▶│  LibreChat API   │ ───────▶│   MongoDB    │
│  (Bun + Vite)    │  HTTPS  │  /api/admin/*    │         │  (shared DB) │
└──────────────────┘         └──────────────────┘         └──────────────┘
       │                             │
       │ OAuth/OIDC/SAML redirect    │ Verifies admin access
       └─────────────────────────────┘
```

The admin panel runs as a separate service; it does not share a process with LibreChat. Admin capabilities are verified on the LibreChat side via the `access:admin` system grant or `SystemRoles.ADMIN` role, so the panel cannot grant itself privileges it shouldn't have.

The admin API surface exposed by LibreChat is:

| Mount                                     | Purpose                                                   |
| ----------------------------------------- | --------------------------------------------------------- |
| `POST /api/admin/login` &nbsp; `/oauth/*` | Admin-specific authentication endpoints (local + SSO)     |
| `GET /api/admin/verify`                   | Validates the admin session                               |
| `/api/admin/users`                        | User listing and search                                   |
| `/api/admin/groups`                       | Group CRUD + member management                            |
| `/api/admin/roles`                        | Custom role CRUD + permission editing + member management |
| `/api/admin/grants`                       | System capability grants (assign/revoke/list)             |
| `/api/admin/config`                       | Base + per-principal configuration overrides              |

## Getting Started

### Prerequisites

- A running LibreChat instance on **v0.8.5 or later** (admin APIs are not available in earlier versions)
- Network access from the admin-panel container/host to the LibreChat API
- An admin account on LibreChat: either the first user registered in an unscoped single-tenant deployment (auto-admin), a user with `role: 'ADMIN'` set in Mongo, or a principal that has been granted the `access:admin` capability. Tenant-scoped deployments must provision their administrators through a trusted administrative flow.

### Bundled with LibreChat (recommended)

If you run LibreChat with its official [`docker-compose.yml`](https://github.com/danny-avila/LibreChat/blob/main/docker-compose.yml) or [`deploy-compose.yml`](https://github.com/danny-avila/LibreChat/blob/main/deploy-compose.yml), the admin panel ships as a service and starts automatically alongside LibreChat -- no separate deployment needed.

| Compose file                   | Admin panel URL          | How it is served                                                  |
| ------------------------------ | ------------------------ | ----------------------------------------------------------------- |
| `docker-compose.yml` (default) | `http://localhost:3000`  | Published on a host port (`ADMIN_PANEL_PORT`, default `3000`)     |
| `deploy-compose.yml`           | `http://admin.localhost` | Routed through the bundled nginx reverse proxy on a subdomain     |

Set the panel's session secret in LibreChat's `.env` before starting the stack; the compose files pass it through as the panel's `SESSION_SECRET`:

```bash filename=".env"
# Min 32 characters. Generate with: openssl rand -hex 32
ADMIN_PANEL_SESSION_SECRET=replace-with-a-32-char-random-string

# Optional: host port for the default docker-compose
# ADMIN_PANEL_PORT=3000

# Optional: set true when the panel is served over HTTPS
# ADMIN_PANEL_SESSION_COOKIE_SECURE=false
```

The compose files wire the rest automatically: `API_SERVER_URL` points at the `api` service, `VITE_API_BASE_URL` follows `DOMAIN_CLIENT` for browser-facing OAuth redirects, and `ADMIN_PANEL_URL` is set so LibreChat returns admins to the panel after SSO. To opt out, remove the `admin-panel` service or gate it behind a Compose [`profiles`](https://docs.docker.com/compose/how-tos/profiles/) entry.

<Callout type="info" title="admin.localhost on a real domain">
  Modern browsers resolve `*.localhost` (including `admin.localhost`) to `127.0.0.1`, so the
  deploy-compose URL works with no hosts-file change. For a real domain, point a DNS record at the
  host, update the `admin.localhost` `server_name` in `client/nginx.conf`, and set `ADMIN_PANEL_URL`
  to match.
</Callout>

### Standalone (separate deployment)

To host the admin panel on its own -- pointed at a LibreChat instance running elsewhere -- use the published image from GHCR:

```bash
# 1. Create an env file
cp .env.example .env

# 2. Edit .env and set at minimum:
#    SESSION_SECRET=<random string, min 32 characters>
#    VITE_API_BASE_URL=http://host.docker.internal:3080

# 3. Start it
docker compose up -d   # http://localhost:3000
docker compose down    # stop
```

Standalone `docker run`:

```bash
docker run -p 3000:3000 \
  --add-host=host.docker.internal:host-gateway \
  -e SESSION_SECRET=replace-with-32-char-random-string \
  -e VITE_API_BASE_URL=http://host.docker.internal:3080 \
  ghcr.io/clickhouse/librechat-admin-panel:latest
```

<Callout type="warning" title="Docker Networking">
  Inside a container, `localhost` refers to the container itself, not your host. When LibreChat runs
  on the same host, point `VITE_API_BASE_URL` at `http://host.docker.internal:3080` (Linux: add
  `--add-host=host.docker.internal:host-gateway`). In production, use the public/internal DNS name
  of your LibreChat API.
</Callout>

### Run Locally for Development

```bash
git clone https://github.com/ClickHouse/librechat-admin-panel.git
cd librechat-admin-panel
cp .env.example .env    # then edit
bun install
bun dev                 # http://localhost:3000
```

## Environment Variables

| Variable                        | Required              | Default                                                                           | Description                                                                                                                                                                               |
| ------------------------------- | --------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SESSION_SECRET`                | **Yes** in production | Hardcoded dev fallback when running `bun dev`; **no default** in the Docker image | Session encryption key. Must be at least 32 characters.                                                                                                                                   |
| `VITE_API_BASE_URL`             | **Yes** in Docker     | `http://localhost:3080` (local dev only)                                          | Browser-facing URL of the LibreChat API server, used for OAuth redirects.                                                                                                                 |
| `API_SERVER_URL`                | No                    | Falls back to `VITE_API_BASE_URL`                                                 | Server-side URL for LibreChat API calls. Useful when the admin-panel server reaches LibreChat on a different URL than the browser (e.g. internal Kubernetes service vs. public hostname). |
| `PORT`                          | No                    | `3000`                                                                            | Port the admin panel listens on.                                                                                                                                                          |
| `ADMIN_PANEL_SESSION_SECRET`    | **Yes**               | _unset_                                                                           | LibreChat-side variable mapped to the admin panel's `SESSION_SECRET` for the bundled admin-panel service. Generate a unique value of at least 32 characters before starting the panel.    |
| `ADMIN_PANEL_PORT`              | No                    | `3000`                                                                            | Host port exposed by the bundled admin-panel service in the default `docker-compose.yml`.                                                                                                  |
| `ADMIN_SSO_ONLY`                | No                    | `false`                                                                           | Hide the email/password form, forcing SSO-only login.                                                                                                                                     |
| `ADMIN_SESSION_IDLE_TIMEOUT_MS` | No                    | `1800000` (30 min)                                                                | Session idle timeout in milliseconds.                                                                                                                                                     |
| `SESSION_COOKIE_SECURE`         | No                    | `true` in production                                                              | Whether the session cookie requires HTTPS.                                                                                                                                                |
| `ADMIN_PANEL_METRICS_SECRET`    | No                    | _unset_                                                                           | Bearer token required to scrape the `/metrics` Prometheus endpoint. The endpoint returns `401` when unset or mismatched.                                                                  |

In LibreChat's bundled Docker stacks, the admin panel runs as an `admin-panel` service. The default `docker-compose.yml` exposes it on `ADMIN_PANEL_PORT`; `deploy-compose.yml` routes it through nginx at `http://admin.localhost` and sets `ADMIN_PANEL_URL` for the API service. The panel refuses to start until `ADMIN_PANEL_SESSION_SECRET` is configured.

### LibreChat Redirect URL

When the admin panel is hosted on a separate URL from LibreChat, set `ADMIN_PANEL_URL` in the LibreChat API environment. Use the external admin panel base URL, including any path prefix, and omit the trailing slash:

```bash filename=".env"
ADMIN_PANEL_URL=https://admin.example.com/admin
```

When set, LibreChat also shows an **Admin Panel** link to administrators under **Settings -> General**. Non-admin users do not see the link.

For Helm deployments, set `librechat.adminPanelUrl` in your values file. The chart renders it as `ADMIN_PANEL_URL` for LibreChat's admin OAuth flow:

```yaml filename="values.yaml"
librechat:
  adminPanelUrl: https://admin.example.com/admin
```

For OpenID SSO, register `${DOMAIN_SERVER}/api/admin/oauth/openid/callback` with your identity provider.

### Cache Controls

These mirror LibreChat's cache env vars. `ADMIN_PANEL_*` variants take precedence, falling back to the shared LibreChat equivalents when unset.

| Variable                                                        | Purpose                                                                                 |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `STATIC_CACHE_MAX_AGE` / `ADMIN_PANEL_STATIC_CACHE_MAX_AGE`     | Browser `max-age` in seconds for hashed assets in `/assets/` (default 172800 = 2 days). |
| `STATIC_CACHE_S_MAX_AGE` / `ADMIN_PANEL_STATIC_CACHE_S_MAX_AGE` | CDN `s-maxage` in seconds (default 86400 = 1 day).                                      |
| `INDEX_CACHE_CONTROL` / `ADMIN_PANEL_INDEX_CACHE_CONTROL`       | `Cache-Control` header for the HTML index response.                                     |
| `INDEX_PRAGMA` / `ADMIN_PANEL_INDEX_PRAGMA`                     | `Pragma` header for the HTML index response.                                            |
| `INDEX_EXPIRES` / `ADMIN_PANEL_INDEX_EXPIRES`                   | `Expires` header for the HTML index response.                                           |

## Authentication

The admin panel reuses LibreChat's authentication stack and does not have its own user database. Two login paths are supported:

- **Local accounts**: username/password against any LibreChat user whose account passes the admin-access check.
- **Single sign-on**: OpenID Connect, SAML, and the social OAuth providers already configured on your LibreChat instance. Set `ADMIN_SSO_ONLY=true` to hide the password form entirely.

Admin access is verified server-side by LibreChat for every request. The account must either:

1. Have `role: 'ADMIN'` in MongoDB, **or**
2. Hold the `access:admin` system grant (assigned to another principal via the admin panel itself; see [System Grants](/docs/features/access_control#layer-3-system-grants-admin-capabilities)).

Sessions are cookie-based, encrypted with `SESSION_SECRET`, and idle-expire per `ADMIN_SESSION_IDLE_TIMEOUT_MS`.

Google admin SSO requests offline access and consent so the panel can refresh an expired LibreChat admin token without forcing an immediate new login. Configure both `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`, and register `${DOMAIN_SERVER}/api/admin/oauth/google/callback` with Google. Each refresh rechecks the account's provider identity, ban state, allowed email domain, tenant, and current admin access; losing access ends the admin session.

## Configuration Management

The panel renders the LibreChat config as a dynamic form driven by the config schema. This has two useful properties:

- **Forward-compatible**: when LibreChat ships a new config field, the panel picks it up automatically from the schema. No admin-panel upgrade or redeploy is required.
- **Layered overrides**: the base config (from `librechat.yaml`) can be shadowed by per-principal overrides scoped to a role or group. When a user logs in, overrides are resolved in priority order and merged on top of the base to produce the effective config that user sees.

<Callout type="warning" title="Process-backed MCP servers stay in YAML">
  MCP servers that use `type: stdio` or process fields such as `command`, `args`, `env`, `cwd`, or `stderr` are operator-owned. Configure them only in `librechat.yaml`; the admin API rejects attempts to create or modify them, and overrides cannot replace or remove them. Remote MCP server configuration remains available through the normal override system.
</Callout>

<Callout type="warning" title="Langfuse gateway headers stay in YAML">
  Deployment-level `langfuse.headers` may contain proxy credentials and are not a Mongo-backed
  configuration surface. Configure them in `librechat.yaml`; the admin API rejects the whole map
  and individual header paths. See [Authenticated Proxies and
  Gateways](/docs/configuration/langfuse#authenticated-proxies-and-gateways).
</Callout>

This is the surface behind LibreChat's [DB-backed per-principal configuration override system](https://github.com/danny-avila/LibreChat/pull/12354). Typical use cases:

- Give a "Research" group higher `recursionLimit` and additional endpoints
- Let a "FinanceAdmins" role manage remote MCP servers while regular users can only use them
- Scope stricter `interface` permissions to external-contractor groups

Administrators can also use the opt-in [Admin Insights](/docs/features/insights) dashboard to review tenant-scoped MongoDB activity. Set `ENABLE_INSIGHTS=true`, then grant both `access:admin` and `read:insights` to an account with the `ADMIN` role.

### Section-Scoped Delegation

Configuration grants can be broad (`read:configs`, `manage:configs`) or limited to one top-level section (`read:configs:<section>`, `manage:configs:<section>`). A section-scoped reader receives a filtered configuration response containing only authorized sections instead of being denied the entire request. Section-level manage grants imply read access to the same section; broad manage access implies broad read access.

Examples include `read:configs:interface`, `manage:configs:endpoints`, and `manage:configs:langfuse`. Delegated users still need `access:admin` to enter the admin surface. Every create, patch, tombstone, or delete mutation of the base `__base__` profile requires broad `manage:configs`; section-scoped management applies to role, group, and user overrides.

### Stored Secrets

When registered secret fields are written through the admin configuration API, LibreChat encrypts literal values at rest and returns only server-generated masked previews. This currently covers Langfuse, OCR, speech, web-search, Assistants, Azure Assistants, and each `endpoints.custom[].apiKey`. Omitting a secret during a later edit preserves the stored value.

Supported environment references remain readable references for fields that allow them. Custom endpoint API keys also preserve `${ENV_VAR}` and `user_provided` values instead of encrypting them. Secret handling is registry-based: arbitrary values in generic maps such as custom headers or MCP configuration are not automatically encrypted, so continue to use environment references or the feature's dedicated credential mechanism there.

## Related

- [Access Control](/docs/features/access_control): the permission model the admin panel is built on
- [Interface Configuration](/docs/configuration/librechat_yaml/object_structure/interface): the feature flags the panel edits
- [Authentication](/docs/features/authentication): user authentication on LibreChat
- [v0.8.5 changelog](/changelog/v0.8.5): admin API foundations
- [GitHub: ClickHouse/librechat-admin-panel](https://github.com/ClickHouse/librechat-admin-panel): source, issues, releases


# Admin Insights (https://www.librechat.ai/docs/features/insights)

Admin Insights is an opt-in dashboard for reviewing LibreChat activity stored in the deployment's primary MongoDB. It reports conversations, unique users, persisted messages, and recorded tokens over time, with top-user, churn, and recent-conversation views.

## Enable Insights

Set `ENABLE_INSIGHTS=true` in `.env`, then restart LibreChat:

```sh filename=".env"
ENABLE_INSIGHTS=true
```

When enabled for an authorized account, **Insights** appears in the desktop sidebar and mobile panel switcher. When disabled, the access and data routes return `404` and the navigation item stays hidden.

## Access Requirements

The signed-in account must satisfy all three checks:

- Have the `ADMIN` role
- Hold the `access:admin` system capability
- Hold the `read:insights` system capability

Use the [Admin Panel](/docs/features/admin_panel) to assign system capabilities. Every Insights query derives tenant scope from the authenticated administrator. Tenant administrators only see records for their own tenant; an administrator without a tenant sees only unscoped records.

<Callout type="warning" title="Sensitive operational data">
  Insights can display user names and email addresses, conversation IDs, and the first message from
  recent conversations. Grant `read:insights` only to administrators who should review this data.
</Callout>

## Dashboard Metrics

The four summary metrics and their daily sparklines use the selected date range:

- **Conversations:** conversations created in the range
- **Unique users:** users represented by matching persisted message activity
- **Messages:** persisted user and assistant messages attributed to those users
- **Tokens:** the sum of recorded message `tokenCount` values

The dashboard also includes:

- **Top users:** up to eight users ordered by message count, with their conversation and message totals
- **Churned users:** up to eight users whose latest message sent to an assistant became 28 days old during the selected range
- **Latest conversations:** paginated conversations with date, owner, first message, persisted message count, and recorded tokens

These are database activity metrics, not billing or Langfuse analytics. Token totals depend on the values persisted with messages and do not calculate monetary cost.

## Date Ranges and Search

The default range is seven days. Presets cover the last 24 hours, 7 days, or 30 days. A custom range can span at most 30 calendar days and is grouped in the browser's current time zone.

Recent-conversation search matches a conversation ID or user identity. Search starts at three characters, accepts up to 200 characters, and keeps the latest-conversation table paginated at 10 rows per page. Summary cards, top users, and churn metrics continue to represent the selected date range rather than the search result subset.

## Data and Deployment Notes

Insights queries the existing Conversation, Message, and User collections directly and requires no separate analytics database. Results reflect persisted records only: temporary or deleted data that is no longer in MongoDB cannot appear. The API bounds custom ranges, result lists, page sizes, search input, and tenant scope before running its aggregations.

See [`ENABLE_INSIGHTS`](/docs/configuration/dotenv#admin-insights) for the deployment switch.


# Password Reset (https://www.librechat.ai/docs/features/password_reset)

<div
  style={{
    padding: '20px',
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'column',
  }}
>
  <img
    src="https://github.com/danny-avila/LibreChat/assets/32828263/498c588c-6e50-4aed-9815-5e06a5409966"
    alt="password reset"
    style={{ borderRadius: '10px' }}
  />
</div>

## Overview

This feature enables email-based password reset functionality for your LibreChat server. You can configure it to work with various email services, including Mailgun API, Gmail, and custom mail servers.

## Key Features

- Supports multiple email services:
  - **Mailgun API** - Recommended for servers that block SMTP ports
  - **Gmail** and other predefined SMTP services
  - **Custom mail servers** with advanced SMTP configuration
- Allows for basic and advanced configuration options
- Enables secure email-based password reset functionality for your LibreChat server

## Setup Options

- **Mailgun Configuration**: Use Mailgun's API for reliable email delivery, especially useful when SMTP ports are blocked
- **Basic SMTP Configuration**: Use predefined services like Gmail with minimal configuration
- **Advanced SMTP Configuration**: Configure generic SMTP services or customize settings for any mail provider

**For detailed setup instructions, refer to the configuration guide: [Email Setup](/docs/configuration/authentication/email)**


# Automated Moderation (https://www.librechat.ai/docs/features/mod_system)

## Automated Moderation System (optional)
The Automated Moderation System uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.

In production, you should have Cloudflare or some other DDoS protection in place to really protect the server from excessive requests, but these changes will largely protect you from the single or several bad actors targeting your deployed instance for proxying.

### Notes

- Uses Caching for basic security and violation logging (bans, concurrent messages, exceeding rate limits)
    - In the near future, I will add **Redis** support for production instances, which can be easily injected into the current caching setup
- Exceeding any of the rate limiters (login/registration/messaging) is considered a violation, default score is 1
- Non-browser origin is a violation
- Default score for each violation is configurable
- Enabling any of the limiters and/or bans enables caching/logging
- Violation logs can be found in the data folder, which is created when logging begins: `librechat/data`
  - **Only violations are logged**
  - `violations.json` keeps track of the total count for each violation per user
  - `logs.json` records each individual violation per user
- Ban logs are stored in MongoDB under the `logs` collection. They are transient as they only exist for the ban duration
    - If you would like to remove a ban manually, you would have to remove them from the database manually and restart the server
    - **Redis** support is also planned for this.

### Rate Limiter Types

#### Login and Registration Rate Limiting
Prevents brute force attacks and spam registrations by limiting how many login attempts or new account registrations can be made from a single IP address within a time window.

#### Message Rate Limiting
Controls how frequently users can send messages to prevent spam and abuse:
- **Concurrent Message Limiting**: Limits how many messages a user can send simultaneously (prevents users from opening multiple tabs to bypass limits)
- **Message Frequency Limiting**: Controls how often messages can be sent, configurable by both IP address and individual user

#### Import Conversation Rate Limiting
Prevents abuse of the conversation import feature by limiting how many conversations can be imported within a time window. This helps prevent:
- Mass data imports that could overwhelm the server
- Automated scripts attempting to flood the system with imported data
- Resource exhaustion from processing large numbers of imports

Default limits:
- IP-based: 100 imports per minute
- User-based: 50 imports per minute (disabled by default)

#### Conversation Forking Rate Limiting
Controls how often users can create forks (copies) of existing conversations. This prevents:
- Excessive database growth from mass conversation duplication
- Resource exhaustion from fork operations
- Abuse of the forking feature for spam or data harvesting

Default limits:
- IP-based: 30 forks per minute
- User-based: 7 forks per minute (disabled by default)

#### File Upload Rate Limiting
Configured through the librechat.yaml file, this controls how often users can upload files to prevent storage abuse and bandwidth exhaustion.

#### Text-to-Speech (TTS) Rate Limiting
Controls how often users can request text-to-speech conversions. This prevents:
- Excessive API usage costs
- Server resource exhaustion from audio generation
- Abuse of the TTS feature for data harvesting

Configured through the librechat.yaml file with customizable limits per IP and per user.

#### Speech-to-Text (STT) Rate Limiting
Controls how often users can submit audio for transcription. This prevents:
- Excessive API usage costs
- Server resource exhaustion from audio processing
- Abuse of the STT feature for unauthorized transcription services

Configured through the librechat.yaml file with customizable limits per IP and per user.

#### Password Reset Rate Limiting
Controls how often users can request password reset emails. This prevents:
- Email bombing attacks
- Abuse of the password reset system
- Excessive email service usage

#### Email Verification Rate Limiting
Controls how often users can request email verification messages. This prevents:
- Spam attacks through the verification system
- Email service abuse
- Resource exhaustion from verification requests

#### Tool Call Rate Limiting
Controls how often users can make tool/plugin calls. This prevents:
- Excessive API usage from integrated tools
- Abuse of external service integrations
- Resource exhaustion from tool processing

#### Conversation Access Rate Limiting
Controls how often users can access or attempt to access conversations. This prevents:
- Unauthorized access attempts
- Data scraping attacks
- Excessive database queries

### Rate Limiters

The project's current rate limiters are as follows (see below under setup for default values):

- Login and registration rate limiting
- `Optional:` Concurrent Message limiting (only X messages at a time per user)
- `Optional:` Message limiting (how often a user can send a message, configurable by IP and User)
- `Optional:` Import conversation limiting (how often a user can import conversations, configurable by IP and User)
- `Optional:` Conversation forking limiting (how often a user can fork conversations, configurable by IP and User)
- `Optional:` Text-to-Speech (TTS) limiting (configurable through [`librechat.yaml` config file](/docs/configuration/librechat_yaml/object_structure/config#ratelimits))
- `Optional:` Speech-to-Text (STT) limiting (configurable through [`librechat.yaml` config file](/docs/configuration/librechat_yaml/object_structure/config#ratelimits))
- `Optional:` File Upload limiting (configurable through [`librechat.yaml` config file](/docs/configuration/librechat_yaml/object_structure/config#ratelimits))

**For further details, refer to the configuration guide provided here: [Automated Moderation](/docs/configuration/mod_system)**

# Compatibility Matrix (https://www.librechat.ai/docs/compatibility)

LibreChat connects to many AI providers through a small set of **endpoint types**.
This page summarizes which capabilities each endpoint supports. Many capabilities
depend on the specific model you select, so those cells are marked as
model-dependent rather than a hard yes or no.

<CompatibilityMatrix />

## Notes

- **Custom (OpenAI-compatible)** covers any provider configured through
  [custom endpoints](/docs/configuration/librechat_yaml/ai_endpoints), such as
  OpenRouter, Groq, Mistral, Ollama, DeepSeek, Perplexity, and others. Exact
  support depends on how closely the provider mirrors the OpenAI API and on the
  model you pick.
- **Agents** is the most capable endpoint: it adds tool use,
  [MCP](/docs/features/mcp), [Code Interpreter](/docs/features/code_interpreter),
  file search, and [Image Generation](/docs/features/image_gen) on top of most
  base models. Reach for it when you need capabilities a plain chat endpoint
  doesn't expose.
- **Vision** and **tools / function calling** are properties of the *model*, not
  the endpoint, so a capable model is required regardless of provider.
- **Assistants** uses OpenAI's Assistants runtime, which provides its own file
  search and code execution but does not layer on LibreChat features such as
  Memory or Artifacts.

This is a manually maintained quick reference. If a value looks wrong for your
release, please [open an issue or PR](https://github.com/LibreChat-AI/librechat.ai).


# MCP Server Guides (https://www.librechat.ai/docs/mcp_servers)

Use these guides to configure specific MCP servers with the right transport, OAuth callbacks, scopes, environment variables, and LibreChat settings.

<Cards num={3}>
  <Cards.Card title="Google Workspace MCP" href="/docs/mcp_servers/google_workspace" arrow>
    Configure Gmail, Drive, Calendar, People, and Chat remote MCP servers with Google OAuth.
  </Cards.Card>
  <Cards.Card title="Salesforce MCP" href="/docs/mcp_servers/salesforce" arrow>
    Configure Salesforce Hosted MCP servers with an External Client App and per-user OAuth.
  </Cards.Card>
  <Cards.Card title="MCP overview" href="/docs/features/mcp" arrow>
    Learn how MCP works in LibreChat before adding product-specific servers.
  </Cards.Card>
</Cards>


# Google Workspace MCP (https://www.librechat.ai/docs/mcp_servers/google_workspace)

Google provides remote Model Context Protocol (MCP) servers for Google Workspace products. In LibreChat, each Google Workspace product is configured as its own OAuth-enabled remote MCP server.

<Callout type="warning" title="Developer Preview">
  Google marks the Workspace MCP servers as part of the Google Workspace Developer Preview Program.
  Review Google's current documentation before deploying this broadly, because available products,
  scopes, and verification requirements may change.
</Callout>

## What You Will Configure

Google Workspace MCP is not one combined server. Configure only the products you want to expose to users:

| Product         | MCP server URL                              |
| --------------- | ------------------------------------------- |
| Gmail           | `https://gmailmcp.googleapis.com/mcp/v1`    |
| Google Drive    | `https://drivemcp.googleapis.com/mcp/v1`    |
| Google Calendar | `https://calendarmcp.googleapis.com/mcp/v1` |
| People API      | `https://people.googleapis.com/mcp/v1`      |
| Google Chat     | `https://chatmcp.googleapis.com/mcp/v1`     |

Each user connects each server from the LibreChat UI. LibreChat stores OAuth tokens per user, so Gmail, Drive, Calendar, People, and Chat access follows the Google account that authorized the connection.

## Prerequisites

- A Google Cloud project.
- Permission to enable APIs and create OAuth clients in that project.
- `gcloud` installed and authenticated, or access to the Google Cloud console.
- A running LibreChat instance with `librechat.yaml` mounted or otherwise loaded.
- The public base URL users use to open LibreChat, for example `http://localhost:3080` for local development or `https://chat.example.com` for production.

<Callout type="info" title="OAuth callback path">
  LibreChat's MCP OAuth callback path is `BASE_URL/api/mcp/SERVER_NAME/oauth/callback`.
  `SERVER_NAME` is the key under `mcpServers` in `librechat.yaml`, such as `gmail` or `drive`.
</Callout>

## Setup

<Steps>
  <Step>

### Enable the Google Workspace APIs

Replace `PROJECT_ID` with your Google Cloud project ID:

```bash
gcloud services enable gmail.googleapis.com \
  drive.googleapis.com \
  calendar-json.googleapis.com \
  chat.googleapis.com \
  people.googleapis.com \
  --project=PROJECT_ID
```

  </Step>
  <Step>

### Enable the Google Workspace MCP services

Enable the MCP services for the products you plan to configure:

```bash
gcloud services enable gmailmcp.googleapis.com \
  drivemcp.googleapis.com \
  calendarmcp.googleapis.com \
  chatmcp.googleapis.com \
  people.googleapis.com \
  --project=PROJECT_ID
```

  </Step>
  <Step>

### Configure Google Chat, if needed

The Google Chat MCP server requires a Chat app in the same Google Cloud project.

In the Google Cloud console, open **Google Chat API** > **Manage** > **Configuration** and create a Chat app:

- **App name**: `Chat MCP`
- **Avatar URL**: `https://developers.google.com/chat/images/quickstart-app-avatar.png`
- **Description**: `Chat MCP server`
- **Functionality**: turn off **Enable interactive features**
- **Logs**: select **Log errors to Logging**

Click **Save**.

<Callout type="warning" title="Workspace account required for Chat">
  Google Chat app configuration may be unavailable for consumer Google accounts. If the console says
  that the Google Chat API is only available to Google Workspace users, omit the `chat` MCP server
  or use a Workspace-backed project/account.
</Callout>

  </Step>
  <Step>

### Configure the Google Auth Platform

In the Google Cloud console, go to **Google Auth Platform**.

If the Google Auth Platform is not configured yet, click **Get Started** and provide:

- **App name**: use a clear name, such as `LibreChat Google Workspace MCP`.
- **User support email**: your support email or Google group.
- **Audience**: choose **Internal** for a Google Workspace organization, or **External** if users are outside your organization or you are using a personal Google account.
- **Developer contact information**: an email where Google can notify you about the project.

If you choose **External** and keep the app in testing mode, add yourself and any other allowed users under **Audience** > **Test users**.

  </Step>
  <Step>

### Add Data Access scopes

Open **Data Access** > **Add or Remove Scopes**. Under **Manually add scopes**, paste the scopes for the servers you want to use.

```text
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.compose
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/calendar.calendarlist.readonly
https://www.googleapis.com/auth/calendar.events.freebusy
https://www.googleapis.com/auth/calendar.events.readonly
https://www.googleapis.com/auth/directory.readonly
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/contacts.readonly
https://www.googleapis.com/auth/chat.spaces.readonly
https://www.googleapis.com/auth/chat.memberships.readonly
https://www.googleapis.com/auth/chat.messages.readonly
https://www.googleapis.com/auth/chat.messages.create
https://www.googleapis.com/auth/chat.users.readstate.readonly
```

Click **Add to Table**, **Update**, then **Save**.

<Callout type="warning" title="Sensitive and restricted scopes">
  Gmail, Drive, Chat, Contacts, and Directory scopes can trigger Google's sensitive or restricted
  scope review. For personal or limited development use, users may see an unverified app warning and
  a 100-user cap. For public or organization-wide use, complete Google's OAuth verification process
  and any required restricted-scope review.
</Callout>

  </Step>
  <Step>

### Create a Web application OAuth client

In **Google Auth Platform** > **Clients**, create an OAuth client:

- **Application type**: `Web application`
- **Name**: use a descriptive name, such as `LibreChat Google Workspace MCP`

Add an authorized redirect URI for every server you configure. For local development:

```text
http://localhost:3080/api/mcp/gmail/oauth/callback
http://localhost:3080/api/mcp/drive/oauth/callback
http://localhost:3080/api/mcp/calendar/oauth/callback
http://localhost:3080/api/mcp/people/oauth/callback
http://localhost:3080/api/mcp/chat/oauth/callback
```

For production, replace `http://localhost:3080` with your LibreChat URL:

```text
https://chat.example.com/api/mcp/gmail/oauth/callback
```

Click **Create**, then copy the **Client ID** and **Client secret**.

  </Step>
  <Step>

### Add OAuth credentials to `.env`

Add the OAuth client values to your LibreChat `.env` file:

```bash filename=".env"
GOOGLE_WORKSPACE_MCP_CLIENT_ID=your-oauth-client-id
GOOGLE_WORKSPACE_MCP_CLIENT_SECRET=your-oauth-client-secret
```

You can use different environment variable names if you also update the `librechat.yaml` references.

  </Step>
  <Step>

### Add the MCP servers to `librechat.yaml`

Add the servers you want under `mcpServers`. This example uses all currently documented Google Workspace MCP servers:

```yaml filename="librechat.yaml"
mcpServers:
  gmail:
    type: streamable-http
    url: 'https://gmailmcp.googleapis.com/mcp/v1'
    timeout: 60000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
      token_url: 'https://oauth2.googleapis.com/token'
      client_id: '${GOOGLE_WORKSPACE_MCP_CLIENT_ID}'
      client_secret: '${GOOGLE_WORKSPACE_MCP_CLIENT_SECRET}'
      scope: 'https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.compose'
      redirect_uri: 'http://localhost:3080/api/mcp/gmail/oauth/callback'
      token_exchange_method: default_post
      token_endpoint_auth_methods_supported: ['client_secret_post']

  drive:
    type: streamable-http
    url: 'https://drivemcp.googleapis.com/mcp/v1'
    timeout: 60000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
      token_url: 'https://oauth2.googleapis.com/token'
      client_id: '${GOOGLE_WORKSPACE_MCP_CLIENT_ID}'
      client_secret: '${GOOGLE_WORKSPACE_MCP_CLIENT_SECRET}'
      scope: 'https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.file'
      redirect_uri: 'http://localhost:3080/api/mcp/drive/oauth/callback'
      token_exchange_method: default_post
      token_endpoint_auth_methods_supported: ['client_secret_post']

  calendar:
    type: streamable-http
    url: 'https://calendarmcp.googleapis.com/mcp/v1'
    timeout: 60000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
      token_url: 'https://oauth2.googleapis.com/token'
      client_id: '${GOOGLE_WORKSPACE_MCP_CLIENT_ID}'
      client_secret: '${GOOGLE_WORKSPACE_MCP_CLIENT_SECRET}'
      scope: 'https://www.googleapis.com/auth/calendar.calendarlist.readonly https://www.googleapis.com/auth/calendar.events.freebusy https://www.googleapis.com/auth/calendar.events.readonly'
      redirect_uri: 'http://localhost:3080/api/mcp/calendar/oauth/callback'
      token_exchange_method: default_post
      token_endpoint_auth_methods_supported: ['client_secret_post']

  people:
    type: streamable-http
    url: 'https://people.googleapis.com/mcp/v1'
    timeout: 60000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
      token_url: 'https://oauth2.googleapis.com/token'
      client_id: '${GOOGLE_WORKSPACE_MCP_CLIENT_ID}'
      client_secret: '${GOOGLE_WORKSPACE_MCP_CLIENT_SECRET}'
      scope: 'https://www.googleapis.com/auth/directory.readonly https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/contacts.readonly'
      redirect_uri: 'http://localhost:3080/api/mcp/people/oauth/callback'
      token_exchange_method: default_post
      token_endpoint_auth_methods_supported: ['client_secret_post']

  chat:
    type: streamable-http
    url: 'https://chatmcp.googleapis.com/mcp/v1'
    timeout: 60000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
      token_url: 'https://oauth2.googleapis.com/token'
      client_id: '${GOOGLE_WORKSPACE_MCP_CLIENT_ID}'
      client_secret: '${GOOGLE_WORKSPACE_MCP_CLIENT_SECRET}'
      scope: 'https://www.googleapis.com/auth/chat.spaces.readonly https://www.googleapis.com/auth/chat.memberships.readonly https://www.googleapis.com/auth/chat.messages.readonly https://www.googleapis.com/auth/chat.messages.create https://www.googleapis.com/auth/chat.users.readstate.readonly'
      redirect_uri: 'http://localhost:3080/api/mcp/chat/oauth/callback'
      token_exchange_method: default_post
      token_endpoint_auth_methods_supported: ['client_secret_post']
```

If LibreChat is deployed at a public URL, update every `redirect_uri` to match the exact redirect URI registered in Google Cloud.

<Callout type="info" title="Strict MCP domain allowlists">
  If your `librechat.yaml` also configures `mcpSettings.allowedDomains`, add the Google MCP hosts
  you use, such as `gmailmcp.googleapis.com`, `drivemcp.googleapis.com`,
  `calendarmcp.googleapis.com`, `chatmcp.googleapis.com`, and `people.googleapis.com`.
</Callout>

  </Step>
  <Step>

### Restart LibreChat

Restart LibreChat so it reloads `.env` and `librechat.yaml`.

| Deployment | Command                              |
| ---------- | ------------------------------------ |
| Docker     | `docker compose up -d`               |
| Local      | Stop the server, then start it again |

To confirm the servers loaded in Docker, check the API logs:

```bash
docker logs LibreChat --tail 200 | grep MCP
```

  </Step>
  <Step>

### Connect each server in LibreChat

Open LibreChat, then open **MCP Settings** or the **MCP Servers** dropdown in the chat input.

For each Google Workspace server:

1. Click **Connect**.
2. Complete the Google OAuth flow in the browser.
3. Review the requested scopes.
4. Click **Allow**.

After OAuth succeeds, the server's tools become available in chat and in the Agent Builder.

  </Step>
</Steps>

## Testing

Try prompts that target one server at a time:

| Server   | Prompt                                                                     |
| -------- | -------------------------------------------------------------------------- |
| People   | "According to my Google profile, what's my name?"                          |
| Drive    | "Find a file named Marketing Plan and summarize it."                       |
| Gmail    | "Find my latest email about the marketing plan."                           |
| Gmail    | "Draft an email to ariel@example.com saying I approve the marketing plan." |
| Calendar | "When is my next meeting with Ariel?"                                      |
| Chat     | "Search recent Google Chat messages about the marketing plan."             |

## Troubleshooting

| Symptom                                             | What to check                                                                                                                                                                                                           |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Google says `redirect_uri_mismatch`                 | The Google OAuth client redirect URI must exactly match the `redirect_uri` in `librechat.yaml`, including protocol, hostname, port, server name, and path.                                                              |
| LibreChat shows the server but no tools             | Connect the server from the LibreChat UI. OAuth-enabled remote servers expose tools after the user has authenticated.                                                                                                   |
| Google shows an unverified app warning              | This is expected for unverified apps requesting sensitive or restricted scopes. For personal or limited development use, users can continue through the warning until the project reaches Google's unverified user cap. |
| OAuth works in testing but later expires            | External apps in testing mode can receive refresh tokens that expire after 7 days. Publish the app to production for longer-lived refresh tokens, or re-authenticate during development.                                |
| Google Chat configuration is disabled               | Use a Google Workspace-backed project/account for Chat, or omit the `chat` server.                                                                                                                                      |
| MCP requests are blocked by LibreChat domain policy | If `mcpSettings.allowedDomains` is configured, add the Google MCP server hostnames you use.                                                                                                                             |

## Security Notes

- Connect Google Workspace MCP servers only to LibreChat instances you trust.
- Request only the products and scopes users actually need.
- Review assistant-suggested actions before sending email, posting Chat messages, uploading files, or changing calendar events.
- Treat email messages, documents, and chat messages as untrusted input. They can contain indirect prompt injection attempts that try to influence the assistant.
- For public or organization-wide deployments, complete Google OAuth verification and follow your organization's third-party app access controls.

## Related Pages

<Cards num={3}>
  <Cards.Card title="MCP" href="/docs/features/mcp" arrow>
    Learn how MCP servers work in LibreChat.
  </Cards.Card>
  <Cards.Card
    title="MCP Servers Object Structure"
    href="/docs/configuration/librechat_yaml/object_structure/mcp_servers"
    arrow
  >
    Review every available `mcpServers` configuration field.
  </Cards.Card>
  <Cards.Card
    title="Google Workspace MCP servers"
    href="https://developers.google.com/workspace/guides/configure-mcp-servers"
    arrow
  >
    Read Google's official Workspace MCP setup guide.
  </Cards.Card>
</Cards>


# Salesforce MCP (https://www.librechat.ai/docs/mcp_servers/salesforce)

Salesforce Hosted MCP servers let LibreChat users connect to Salesforce through per-user OAuth.
Each tool call runs with the authenticated Salesforce user's permissions, including field-level
security, object permissions, and sharing rules.

<Callout type="warning" title="Use an External Client App">
  Salesforce Hosted MCP servers require an External Client App. Classic Salesforce Connected Apps
  are not supported for Hosted MCP authentication.
</Callout>

## What You Will Configure

This guide starts with the read-only SObject server because it is the safest first connection. After
that works, you can switch to a broader Salesforce server.

| Server            | Access level                       | Production URL                                                          | Sandbox or scratch URL                                                          |
| ----------------- | ---------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| SObject Reads     | Read, query, search, relationships | `https://api.salesforce.com/platform/mcp/v1/platform/sobject-reads`     | `https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-reads`     |
| SObject Mutations | Read, create, update, no delete    | `https://api.salesforce.com/platform/mcp/v1/platform/sobject-mutations` | `https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-mutations` |
| SObject Deletes   | Delete-focused workflows           | `https://api.salesforce.com/platform/mcp/v1/platform/sobject-deletes`   | `https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-deletes`   |
| SObject All       | Full create, read, update, delete  | `https://api.salesforce.com/platform/mcp/v1/platform/sobject-all`       | `https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all`       |

<Callout type="info" title="OAuth callback path">
  LibreChat's MCP OAuth callback path is `BASE_URL/api/mcp/SERVER_NAME/oauth/callback`.
  `SERVER_NAME` is the key under `mcpServers` in `librechat.yaml`. The examples below use
  `salesforce`, so the local callback is `http://localhost:3080/api/mcp/salesforce/oauth/callback`.
</Callout>

## Prerequisites

- A Salesforce org that supports Hosted MCP servers and API access.
- System Administrator permissions, or equivalent permissions to create External Client Apps and enable MCP servers.
- A running LibreChat instance with `librechat.yaml` mounted or otherwise loaded.
- The public base URL users use to open LibreChat, for example `http://localhost:3080` for local development or `https://chat.example.com` for production.

If you do not have a Salesforce org yet, create a free Developer Edition org from
[developer.salesforce.com/signup](https://developer.salesforce.com/signup), verify the account by
email, then log in at [login.salesforce.com](https://login.salesforce.com). If the Salesforce Setup
menus in this guide are not visible in the org, use a supported production, sandbox, or trial org
with Hosted MCP servers enabled.

## Setup

<Steps>
  <Step>

### Activate the Salesforce MCP server

In Salesforce, open **Setup**.

1. In **Quick Find**, search for `MCP Servers`.
2. Open **MCP Servers** under **API Catalog**.
3. Enable the server you want to use. For first setup, enable `platform/sobject-reads`.
4. Wait up to 2 minutes for the server to become active.

If you plan to use the full-access example, enable `platform/sobject-all` instead.

  </Step>
  <Step>

### Create an External Client App

In Salesforce Setup:

1. In **Quick Find**, search for `External Client App Manager`.
2. Click **New External Client App**.
3. Fill out the basic app information. Use a clear name, such as `LibreChat Salesforce MCP`.
4. Expand **API (Enable OAuth Settings)** and enable OAuth.
5. Add the LibreChat callback URL.

For local development:

```text
http://localhost:3080/api/mcp/salesforce/oauth/callback
```

For production, replace the base URL with your public LibreChat URL:

```text
https://chat.example.com/api/mcp/salesforce/oauth/callback
```

  </Step>
  <Step>

### Configure OAuth scopes and security

In the External Client App OAuth settings, add these scopes:

```text
mcp_api
refresh_token
```

Do not use the standard Salesforce `api` scope for Hosted MCP servers. The MCP server expects the
`mcp_api` scope.

In the External Client App security settings:

- Select **Issue JSON Web Token (JWT)-based access tokens for named users**.
- Select **Require Proof Key for Code Exchange (PKCE) extension for Supported Authorization Flows**.
- Leave **Require Secret for Web Server Flow** disabled for the basic setup in this guide.
- Leave **Require Secret for Refresh Token Flow** disabled for the basic setup in this guide.

Click **Create**, then open the app settings and copy the **Consumer Key**. Salesforce says a new
External Client App can take up to 30 minutes to become available.

<Callout type="info" title="Optional client secret">
  LibreChat can store a client secret server-side. If your Salesforce admin enables **Require Secret
  for Web Server Flow**, also generate a client secret and include the optional `client_secret`
  fields shown later in this guide.
</Callout>

  </Step>
  <Step>

### Add the Salesforce client ID to `.env`

Add the External Client App consumer key to your LibreChat `.env` file:

```bash filename=".env"
SALESFORCE_MCP_CLIENT_ID=your-salesforce-consumer-key
```

If you enabled **Require Secret for Web Server Flow**, also add:

```bash filename=".env"
SALESFORCE_MCP_CLIENT_SECRET=your-salesforce-client-secret
```

  </Step>
  <Step>

### Add Salesforce MCP to `librechat.yaml`

This example configures the read-only SObject server for a production or Developer Edition org:

```yaml filename="librechat.yaml"
mcpServers:
  salesforce:
    type: streamable-http
    url: 'https://api.salesforce.com/platform/mcp/v1/platform/sobject-reads'
    timeout: 90000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://login.salesforce.com/services/oauth2/authorize'
      token_url: 'https://login.salesforce.com/services/oauth2/token'
      client_id: '${SALESFORCE_MCP_CLIENT_ID}'
      scope: 'mcp_api refresh_token'
      redirect_uri: 'http://localhost:3080/api/mcp/salesforce/oauth/callback'
```

For a sandbox or scratch org, use the sandbox MCP URL and Salesforce sandbox OAuth endpoints:

```yaml filename="librechat.yaml"
mcpServers:
  salesforce:
    type: streamable-http
    url: 'https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-reads'
    timeout: 90000
    initTimeout: 150000
    requiresOAuth: true
    startup: false
    oauth:
      authorization_url: 'https://test.salesforce.com/services/oauth2/authorize'
      token_url: 'https://test.salesforce.com/services/oauth2/token'
      client_id: '${SALESFORCE_MCP_CLIENT_ID}'
      scope: 'mcp_api refresh_token'
      redirect_uri: 'http://localhost:3080/api/mcp/salesforce/oauth/callback'
```

If your External Client App requires a client secret, add these fields inside `oauth`:

```yaml filename="librechat.yaml"
oauth:
  client_secret: '${SALESFORCE_MCP_CLIENT_SECRET}'
  token_exchange_method: default_post
  token_endpoint_auth_methods_supported: ['client_secret_post']
```

If LibreChat is deployed at a public URL, update `redirect_uri` to match the exact callback URL
registered in Salesforce.

<Callout type="info" title="Strict MCP domain allowlists">
  If your `librechat.yaml` also configures `mcpSettings.allowedDomains`, add `api.salesforce.com`.
  If you use Salesforce sandbox OAuth endpoints and your policy also applies to OAuth hosts, allow
  `test.salesforce.com` or your sandbox My Domain host as well.
</Callout>

  </Step>
  <Step>

### Switch to full Salesforce access, if needed

After the read-only server works, you can switch to another activated Salesforce server by changing
the `url`.

For full SObject access in a production or Developer Edition org:

```yaml filename="librechat.yaml"
url: 'https://api.salesforce.com/platform/mcp/v1/platform/sobject-all'
```

For full SObject access in a sandbox or scratch org:

```yaml filename="librechat.yaml"
url: 'https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all'
```

Only expose mutation or delete-capable servers to users who should be allowed to create, update, or
delete Salesforce records through an assistant.

  </Step>
  <Step>

### Restart LibreChat

Restart LibreChat so it reloads `.env` and `librechat.yaml`.

| Deployment | Command                              |
| ---------- | ------------------------------------ |
| Docker     | `docker compose up -d`               |
| Local      | Stop the server, then start it again |

To confirm the server loaded in Docker, check the API logs:

```bash
docker logs LibreChat --tail 200 | grep MCP
```

  </Step>
  <Step>

### Connect Salesforce in LibreChat

Open LibreChat, then open **MCP Settings** or the **MCP Servers** dropdown in the chat input.

1. Click **Connect** for the Salesforce server.
2. Complete the Salesforce OAuth flow.
3. Review the requested `mcp_api` and `refresh_token` scopes.
4. Return to LibreChat after the OAuth success page closes.

After OAuth succeeds and the MCP connection initializes, Salesforce tools become available in chat
and in the Agent Builder.

  </Step>
</Steps>

## Testing

Try prompts that match the server you enabled:

| Server             | Prompt                                                                           |
| ------------------ | -------------------------------------------------------------------------------- |
| Any SObject server | "Who am I in Salesforce?"                                                        |
| SObject Reads      | "Show me my five most recently viewed accounts."                                 |
| SObject Reads      | "Find open cases related to Acme Corp and summarize them."                       |
| SObject Mutations  | "Create a follow-up task for this account. Ask me before saving anything."       |
| SObject All        | "Update this opportunity stage to Closed Won after confirming the exact record." |

For a lower-level sanity check, test the same Salesforce server with Postman or MCP Inspector. If
that works but LibreChat does not, compare the LibreChat callback URL, OAuth scopes, and server URL
against the working client.

## Troubleshooting

| Symptom                                                           | What to check                                                                                                                                                                                                                                                  |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Salesforce says `redirect_uri_mismatch`                           | The External Client App callback URL must exactly match the `redirect_uri` in `librechat.yaml`, including protocol, hostname, port, server name, and path.                                                                                                     |
| Salesforce Login History says `Invalid Code Verifier`             | PKCE state did not match the callback. Avoid starting multiple OAuth flows at once. In multi-replica deployments, make sure LibreChat uses shared OAuth flow storage, such as Redis, so authorize and callback requests can land on different replicas safely. |
| Salesforce returns `JWT Token is required`                        | The server is being called without a Salesforce MCP access token. Complete OAuth from LibreChat, confirm `requiresOAuth: true`, and confirm the user authorized the External Client App.                                                                       |
| Salesforce returns `Invalid token`                                | Confirm the External Client App uses `mcp_api`, issues JWT-based access tokens, and has PKCE enabled. Also confirm the MCP URL and OAuth endpoints point to the same org type, production versus sandbox.                                                      |
| Salesforce returns `Server definition not found for: sobject-all` | The server is not activated, is still propagating, or the URL uses the wrong production versus sandbox path. Enable the server in Salesforce Setup and wait up to 2 minutes.                                                                                   |
| Token refresh fails with a scope-related error                    | Use `mcp_api refresh_token` as the configured scope. Do not substitute the regular Salesforce `api` scope for Hosted MCP.                                                                                                                                      |
| LibreChat shows Salesforce but no tools                           | Connect the server from the LibreChat UI. OAuth-enabled remote servers expose tools after the user has authenticated and the server has initialized.                                                                                                           |
| MCP requests are blocked by LibreChat domain policy               | If `mcpSettings.allowedDomains` is configured, allow `api.salesforce.com` and any Salesforce OAuth host your deployment uses.                                                                                                                                  |

## Security Notes

- Start with `platform/sobject-reads` unless users truly need write or delete access.
- Salesforce enforces the authenticated user's permissions, but the assistant can still propose broad actions. Review write and delete operations carefully.
- Use Salesforce permission sets and External Client App policies to restrict who can authorize the MCP client.
- Treat Salesforce records as untrusted input. Records can contain indirect prompt injection attempts that try to influence the assistant.
- For production deployments with multiple LibreChat API replicas, use shared storage for OAuth flow state so PKCE callbacks are durable across replicas.

## Related Pages

<Cards num={3}>
  <Cards.Card title="MCP" href="/docs/features/mcp" arrow>
    Learn how MCP servers work in LibreChat.
  </Cards.Card>
  <Cards.Card
    title="MCP Servers Object Structure"
    href="/docs/configuration/librechat_yaml/object_structure/mcp_servers"
    arrow
  >
    Review every available `mcpServers` configuration field.
  </Cards.Card>
  <Cards.Card
    title="Salesforce Hosted MCP servers"
    href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/hosted-mcp-servers-overview.html"
    arrow
  >
    Read Salesforce's official Hosted MCP setup documentation.
  </Cards.Card>
</Cards>


# Overview (https://www.librechat.ai/docs/user_guides)

Whether you are a new user or exploring advanced features, these guides help you get the most out of LibreChat.

## Common Tasks

<Cards num={3}>
  <Cards.Card title="Start Using LibreChat" href="/docs/user_guides/ai_overview" arrow>
    Learn what endpoints, models, presets, and providers mean in the chat UI
  </Cards.Card>
  <Cards.Card title="Create an Agent" href="/docs/features/agents" arrow>
    Build a custom assistant with instructions, files, tools, and capabilities
  </Cards.Card>
  <Cards.Card title="Generate Images" href="/docs/features/image_gen" arrow>
    Create an agent with image tools and generate or edit images from chat
  </Cards.Card>
  <Cards.Card title="Search the Web" href="/docs/features/web_search" arrow>
    Enable web search for current information and source-backed answers
  </Cards.Card>
  <Cards.Card title="Chat with Files" href="/docs/features/rag_api" arrow>
    Use RAG and file search to ask questions about uploaded documents
  </Cards.Card>
  <Cards.Card title="Connect External Tools" href="/docs/features/mcp" arrow>
    Add external services through MCP servers and agent tools
  </Cards.Card>
</Cards>

## Guides

<Cards num={3}>
  <Cards.Card title="AI Overview" href="/docs/user_guides/ai_overview" arrow>
    Understand endpoints, presets, and how AI providers work in LibreChat
  </Cards.Card>
  <Cards.Card title="Presets" href="/docs/user_guides/presets" arrow>
    Save and load predefined conversation settings
  </Cards.Card>
  <Cards.Card title="MongoDB" href="/docs/user_guides/mongodb" arrow>
    Why LibreChat uses MongoDB and how data is stored
  </Cards.Card>
</Cards>

## Popular Features

<Cards num={4}>
  <Cards.Card title="Agents" href="/docs/features/agents" arrow>
    Create AI agents with custom tools and capabilities
  </Cards.Card>
  <Cards.Card title="Image Generation" href="/docs/features/image_gen" arrow>
    Set up and use image generation tools in your agents
  </Cards.Card>
  <Cards.Card title="Web Search" href="/docs/features/web_search" arrow>
    Enable AI-powered web search in conversations
  </Cards.Card>
  <Cards.Card title="MCP" href="/docs/features/mcp" arrow>
    Connect external tools via Model Context Protocol
  </Cards.Card>
</Cards>


# AI Overview (https://www.librechat.ai/docs/user_guides/ai_overview)

LibreChat allows you to configure and integrate various AI providers, APIs, and their corresponding credentials. This enables you to utilize different AI models, settings, and functionalities based on your needs and requirements.

## Key Concepts

- **Endpoints**: An endpoint refers to an AI provider, configuration, or API that determines the available models and settings for a chat request. Examples include OpenAI, Google, Plugins, Anthropic, and others.

- **Presets**: A preset is a saved combination of an endpoint, model, and conversation settings. You can create and manage multiple presets to suit different use cases.

- **Default Endpoint**: If you have multiple endpoints configured, you can specify a default endpoint to be used when creating a new conversation.

- **Default Preset**: Similarly, you can set a default preset to be used automatically when starting a new conversation.

## Functionality

1. **AI Providers**: Set up various pre-configured AI providers by providing the necessary credentials and API keys.

2. **Manage Endpoints**: Enable or disable different endpoints based on your requirements.

3. **Create and Manage Presets**: Define and save specific combinations of endpoints, models, and conversation settings as presets.

4. **Set Default Endpoint and Preset**: Specify a default endpoint and preset to streamline the process of starting new conversations. Here's a video to demonstrate: **[Setting a Default Preset](https://github.com/danny-avila/LibreChat/assets/110412045/bbde830f-18d9-4884-88e5-1bd8f7ac585d)**

5. **Customize Configurations**: Explore advanced configuration options, such as adding custom endpoints like Ollama, Mistral AI or Openrouter.


# Presets (https://www.librechat.ai/docs/user_guides/presets)

# Deprecation Notice

**This feature is deprecated. Please refer to the [Agents Guide](/docs/features/agents), which acts as a successor to Presets**

The "presets" feature in our app is a powerful tool that allows users to save and load predefined settings for their conversations. Users can import and export these presets as JSON files, set a default preset, and share them with others on Discord.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/8c39ad89-71ae-42c6-a792-3db52d539fcd)

## Create a Preset:

- Go in the model settings

![image](https://github.com/danny-avila/LibreChat/assets/32828263/2fc883e9-f4a3-47ac-b375-502e82234194)

- Choose the model, give it a name, some custom instructions, and adjust the parameters if needed

![image](https://github.com/danny-avila/LibreChat/assets/32828263/090dc065-f9ea-4a43-9380-e6d504e64992)

- Test it

![image](https://github.com/danny-avila/LibreChat/assets/32828263/8a383495-0d5e-4ab7-93a7-eca5388c3f6f)

- Go back in the model advanced settings, and tweak it if needed. When you're happy with the result, click on `Save As Preset` (from the model advanced settings)

![image](https://github.com/danny-avila/LibreChat/assets/32828263/96fd88ec-b4b6-4de0-a7d7-f156fdace354)

- Give it a proper name, and click save

![image](https://github.com/danny-avila/LibreChat/assets/32828263/76ad8db4-a949-4633-8a5f-f9e8358d57f3)

- Now you can select it from the preset menu! 

![image](https://github.com/danny-avila/LibreChat/assets/32828263/81271990-2739-4f5c-b1a5-7d7deeaa385c)

## Parameters Explained:

- **Preset Name:**
  - This is where you name your preset for easy identification.

- **Endpoint:**
  - Choose the endpoint, such as openAI, that you want to use for processing the conversation.

- **Model:**
  - Select the model like `gpt-3.5-turbo` that will be used for generating responses.

- **Custom Name:**
  - Optionally provide a custom name for your preset. This is the name that will be shown in the UI when using it.

- **Custom Instructions:**
  - Define instructions or guidelines that will be displayed before each prompt to guide the user in providing input.

- **Temperature:**
  - Adjust this parameter to control the randomness of the model's output. A higher value makes the output more random, while a lower value makes it more focused and deterministic.

- **Top P:**
  - Control the nucleus sampling parameter to influence the diversity of generated text. Lower values make text more focused while higher values increase diversity.

- **Frequency Penalty:**
  - Use this setting to penalize frequently occurring tokens and promote diversity in responses.

- **Presence Penalty:**
  - Adjust this parameter to penalize new tokens that are introduced into responses, controlling repetition and promoting consistency.

## Importing/Exporting Presets

You can easily import or export presets as JSON files by clicking on either 'Import' or 'Export' buttons respectively. This allows you to share your customized settings with others or switch between different configurations quickly.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/b9ef56e2-393e-45eb-b72b-8d568a13a015)

To export a preset, first go in the preset menu, then click on the button to edit the selected preset

![image](https://github.com/danny-avila/LibreChat/assets/32828263/3fb065e6-977b-49b4-9fc6-de55b9839031)

Then in the bottom of the preset settings you'll have the option to export it.

![Saving and selecting a preset](https://github.com/danny-avila/LibreChat/assets/32828263/a624345f-e3e6-4192-8384-293ba6ce54cc)

## Setting Default Preset

Choose a preset as default so it loads automatically whenever you start a new conversation. This saves time if you often use specific settings.

![image](https://github.com/danny-avila/LibreChat/assets/32828263/5912650d-49b6-40d3-b9ad-ff2ff7fbe3e7)
![image](https://github.com/danny-avila/LibreChat/assets/32828263/dcfb5e27-f60b-419e-b387-25db85fa6a63)

## Sharing on Discord

Join us on [discord](https://discord.librechat.ai) and see our **[#presets ](https://discord.com/channels/1086345563026489514/1093249324797935746)** channel where thousands of presets are shared by users worldwide. Check out pinned posts for popular presets!

# MongoDB (https://www.librechat.ai/docs/user_guides/mongodb)

MongoDB, a popular NoSQL database, was chosen as the core database for LibreChat due to its flexibility, scalability, and ability to handle diverse data structures efficiently. Here are some key reasons why MongoDB is an excellent fit for LibreChat:

![MongoDB Compass viewing the LibreChat database](https://github.com/danny-avila/LibreChat/assets/32828263/84d4a608-1f73-41c9-a026-7772a74d205b)

## 1. Flexible Data Model
MongoDB's document-based data model allows for storing and retrieving data in a flexible and dynamic manner. Unlike traditional relational databases, MongoDB doesn't require a fixed schema, making it easier to adapt to changing data requirements. This flexibility is essential for LibreChat, as it needs to store various types of data, such as conversation histories, user profiles, presets, API keys, and more, without being constrained by a rigid table structure.

## 2. Efficient Storage of Conversation Histories
One of the primary use cases for LibreChat is to store and retrieve conversation histories. MongoDB's ability to store nested data structures as JSON-like documents makes it an excellent choice for storing conversation histories, which can include complex data structures like messages, timestamps, and metadata.

## 3. Secure Storage of Sensitive Data
LibreChat handles sensitive data, such as API keys and encrypted user passwords. MongoDB's built-in support for data encryption at rest and in transit ensures that this sensitive information remains secure and protected from unauthorized access.

## 4. Horizontal Scalability
As LibreChat grows and attracts more users, its data storage requirements will increase. MongoDB's horizontal scalability allows for scaling out by adding more servers to a cluster, providing the ability to handle larger amounts of data and higher traffic loads without compromising performance.

## 5. Cross-Device Accessibility
LibreChat aims to provide a seamless experience across multiple devices, allowing users to access their data and conversation histories from different devices. MongoDB's replication and sharding capabilities ensure that data is consistently available and accessible, enabling users to pick up their conversations where they left off, regardless of the device they're using.

## 6. Developer Productivity
MongoDB's intuitive query language and rich ecosystem of tools and libraries contribute to faster development cycles and increased developer productivity. This aligns well with LibreChat's goal of being an open-source project, fostering collaboration and contributions from the developer community.

By leveraging MongoDB's strengths, LibreChat can efficiently manage and store diverse data structures, ensure data security and availability, and provide a seamless cross-device experience for its users. MongoDB's flexibility, scalability, and developer-friendly features make it an ideal choice for powering the core functionalities of LibreChat.

## Amazon DocumentDB Compatibility

LibreChat supports **Amazon DocumentDB 5.0 or later instance-based clusters** as a deployment target. Configure `MONGO_URI` with `retryWrites=false`, because DocumentDB does not support retryable writes:

```bash filename=".env"
MONGO_URI=mongodb://<user>:<password>@<cluster>:27017/librechat?tls=true&tlsCAFile=/path/to/global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false
```

Use the current AWS CA bundle and the network path required for your VPC. See AWS's [functional differences from MongoDB](https://docs.aws.amazon.com/documentdb/latest/developerguide/functional-differences.html) and [connection guidance](https://docs.aws.amazon.com/documentdb/latest/developerguide/connect_programmatically.html).

DocumentDB 4.0 can run LibreChat with reduced database enforcement: it does not support the partial unique indexes LibreChat uses for OAuth identities, generated code files, and external group IDs. LibreChat logs index-build failures at startup, but those constraints are not enforced by the database. DocumentDB 5.0+ instance-based clusters support the required partial-index forms.

<Callout type="warning" title="Elastic clusters are unsupported">
  Amazon DocumentDB elastic clusters do not support unique indexes, including LibreChat's unique
  user identity index, and are not a supported LibreChat database target. See the AWS
  [elastic-cluster limitations](https://docs.aws.amazon.com/documentdb/latest/developerguide/docdb-using-elastic-clusters.html).
</Callout>

Do not run `Model.syncIndexes()` against DocumentDB as an operational migration step; DocumentDB's `collMod` support is limited. LibreChat's regular startup index creation is the intended path.


## Note

<Callout type="warning" title="CPU compatibility">
**Note:** If you're running LibreChat on a processor that doesn't have SSE4.2, AVX support, or other required CPU features, you'll need to use an older but compatible version of MongoDB with the Docker installation. Specifically, you should use the `mongo:4.4.18` image, which is compatible with processors without these features.

To use this older MongoDB version with the LibreChat Docker installation, you'll need to utilize the `docker-compose.override.yml` file. This override file allows you to specify the MongoDB version you want to use, overriding the default version included in the main `docker-compose.yml` file.

For more information on using the `docker-compose.override.yml` file and configuring an older MongoDB version for your Docker installation, please refer to our [Docker Override Configuration Guide](/docs/configuration/docker_override).
</Callout>


# Toolkit (https://www.librechat.ai/docs/toolkit)

<Cards num={2}>
  <Card title="Credentials Generator" href="/docs/toolkit/credentials-generator" arrow>
    Generate secure random values for CREDS_KEY, JWT_SECRET, and other .env variables.
  </Card>
  <Card title="YAML Validator" href="/docs/toolkit/yaml-validator" arrow>
    Paste or drop your librechat.yaml to check for syntax errors and formatting issues.
  </Card>
</Cards>


# Credentials Generator (https://www.librechat.ai/docs/toolkit/credentials-generator)

import { CredentialsGeneratorMDX } from '@/components/tools/CredentialsGeneratorMDX'

Generate cryptographically secure random values for the required secrets in your LibreChat `.env` configuration file. Click **Generate** and then copy individual values or all of them at once.

<CredentialsGeneratorMDX />


# YAML Validator (https://www.librechat.ai/docs/toolkit/yaml-validator)

import { YAMLValidatorMDX } from '@/components/tools/YAMLValidatorMDX'

Paste your `librechat.yaml` content below or drag and drop a file to check for syntax errors. The validator highlights the exact line where issues occur.

<YAMLValidatorMDX />


# Translation (https://www.librechat.ai/docs/translation)

Thank you for your interest in translating LibreChat! We rely on community contributions to make our application accessible to users around the globe. All translations are managed via [Locize](https://locize.com), a robust translation management system that seamlessly integrates with our project.

## How Translations Work

- **Centralized Management:**
All translation strings for LibreChat are maintained in one location on Locize. This centralization ensures consistency and simplifies updates across the entire application.

- **Automatic Updates:**
Changes made in Locize are automatically synchronized with our project. You can monitor the translation progress for each language through dynamic badges in our repository.

- **Community Driven:**
We welcome contributions in every language. Your help makes LibreChat accessible to a broader audience and supports users in their native languages.


## Translation Progress

Below is our current translation progress for some of the supported languages. Feel free to check these badges and help us improve the translations further:

| Language                            | Translation Progress Badge                                                                                                                                                                                                                                                                                       |
|-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **English (en)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'en'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="EN Badge" />           |
| **Arabic (ar)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ar'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="AR Badge" />           |
| **Tibetan (bo)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'bo'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="BO Badge" />           |
| **Bosnian (bs)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'bs'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="BS Badge" />           |
| **Catalan (ca)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ca'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="CA Badge" />           |
| **Czech (cs)**                      | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'cs'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="CS Badge" />           |
| **Danish (da)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'da'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="DA Badge" />           |
| **German (de)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'de'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="DE Badge" />           |
| **Spanish (es)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'es'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="ES Badge" />           |
| **Estonian (et)**                   | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'et'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="ET Badge" />           |
| **Persian (fa)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'fa'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="FA Badge" />           |
| **Finnish (fi)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'fi'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="FI Badge" />           |
| **French (fr)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'fr'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="FR Badge" />           |
| **Hebrew (he)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'he'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="HE Badge" />           |
| **Hungarian (hu)**                  | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'hu'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="HU Badge" />           |
| **Armenian (hy)**                   | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'hy'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="HY Badge" />           |
| **Indonesian (id)**                 | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'id'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="ID Badge" />           |
| **Icelandic (is)**                  | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'is'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="IS Badge" />           |
| **Italian (it)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'it'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="IT Badge" />           |
| **Japanese (ja)**                   | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ja'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="JA Badge" />           |
| **Georgian (ka)**                   | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ka'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="KA Badge" />           |
| **Korean (ko)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ko'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="KO Badge" />           |
| **Lithuanian (lt)**                 | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'lt'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="LT Badge" />           |
| **Latvian (lv)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'lv'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="LV Badge" />           |
| **Norwegian Bokmål (nb)**           | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'nb'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="NB Badge" />           |
| **Norwegian Nynorsk (nn)**          | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'nn'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="NN Badge" />           |
| **Dutch (nl)**                      | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'nl'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="NL Badge" />           |
| **Polish (pl)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'pl'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="PL Badge" />           |
| **Portuguese (pt-PT)**              | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'pt-PT'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="PT-PT Badge" />     |
| **Brazilian Portuguese (pt-BR)**    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'pt-BR'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="PT-BR Badge" />     |
| **Russian (ru)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ru'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="RU Badge" />           |
| **Slovak (sk)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'sk'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="SK Badge" />           |
| **Slovenian (sl)**                  | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'sl'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="SL Badge" />           |
| **Swedish (sv)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'sv'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="SV Badge" />           |
| **Thai (th)**                       | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'th'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="TH Badge" />           |
| **Turkish (tr)**                    | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'tr'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="TR Badge" />           |
| **Uyghur (ug)**                     | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'ug'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="UG Badge" />           |
| **Ukrainian (uk)**                  | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'uk'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="UK Badge" />           |
| **Vietnamese (vi)**                 | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'vi'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="VI Badge" />           |
| **Chinese (Simplified) (zh-Hans)**  | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'zh-Hans'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="ZH-HANS Badge" /> |
| **Chinese (Traditional) (zh-Hant)** | <img src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=2096F3&label=Locize&query=$.versions%5B'latest'%5D.languages%5B'zh-Hant'%5D.translatedPercentage&url=https%3A%2F%2Fapi.locize.app%2Fbadgedata%2F4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%25+translated" alt="ZH-HANT Badge" /> |

---


## Getting Started

Before you begin translating, please follow the steps below to set up your Locize account and start contributing.


### Step 1: Create a Locize Account

1. Visit the Registration Page

    Choose your preferred language and click the corresponding link to register:

    - **[English (en)](https://www.locize.app/register?invitation=tTHVYwI9TzWuOeJnKyWxSuuaDrSS5TsZqRh0BAHzebuuljz5OUXnlqbksio5WnXp)**
    - **[Arabic (ar)](https://www.locize.app/register?invitation=Cn7hc6teYyY89nvQdosfXkubbj2MZi2XMzcxUP6fGglcPkAgd1AwS5Pfbr1Wu4lz)**
    - **[Tibetan (bo)](https://www.locize.app/register?invitation=183Kk5eTUvr0oeKkZqsIrSG1iflQPk5WjZzH5tb1uwDS7MdS3nFo55frHm86lz78)**
    - **[Bosnian (bs)](https://www.locize.app/register?invitation=JA2zNrFrvoEbrXQgoZAxRrEStscwrCCAhTP7t44NBxa7XAJbdhwpqntq3xQ6Wltw)**
    - **[Catalan (ca)](https://www.locize.app/register?invitation=c7ZQ0OT9k6c5kWRXMspgRQZXoZk2vZYIZ3RTpDkVrtShlauJ55YpeMo9EOBsYXgS)**
    - **[Czech (cs)](https://www.locize.app/register?invitation=ypNUvwJG9ynzZabWS4X1bD2b8qSmhbvyXsytm1sbkVmqN0A6inKhiny2FuNVcvC2)**
    - **[Danish (da)](https://www.locize.app/register?invitation=xClNgP4A7SNcMdhkTsf6xm8wP24mNbSW3YhnOtDNd7nMHhAZPJsWV8UIyTR6lLxI)**
    - **[German (de)](https://www.locize.app/register?invitation=rAXIyYNuO53txcygphdOClUR5YnNccd1MZ1Vs66p4ziOqHfM3MFiKnymdK6wLMpW)**
    - **[Spanish (es)](https://www.locize.app/register?invitation=gkrRvUjxvFnRfUtynbaREj2zdbvd2FU95OFGcixGMOkcCxSwmrvOBclBZJWmERw6)**
    - **[Estonian (et)](https://www.locize.app/register?invitation=q1ye9gNpYsVKvs2JS5CEjp4SBy6ovq2aUeIhAMsRoW2iVcdfxpc4GiOaHGDV85VZ)**
    - **[Persian (fa)](https://www.locize.app/register?invitation=DfuKT56Y4KxlPOm6biYl9zbgMo3kBjWJA3Flbd7X5H4hJluUWTsFiQ3MQH07fH0d)**
    - **[Finnish (fi)](https://www.locize.app/register?invitation=weERAttD7ax0Zfo5w9LyfWbaP50WPbS4Vk7BF1P6wM0fe5Q0xlrJACVjTAGbkv2c)**
    - **[French (fr)](https://www.locize.app/register?invitation=wz5EbZiwE9Bxev4TTAyG09PKvnFSQoOaLomxVgEKOyaLhm9oCDdhDi4TkJp8rcq5)**
    - **[Hebrew (he)](https://www.locize.app/register?invitation=3LSHQxUsHYumNhw5ZJNu6Re6x699i6RGnpdKzt2BrDeNFNObxjj2lKXfZFUhb1jP)**
    - **[Hungarian (hu)](https://www.locize.app/register?invitation=3TaJ2SnDnYfTJQLalKK751cQ04AK5A2gRahC2poeZYXj8MymZ5T0rt4V688e0H7M)**
    - **[Armenian (hy)](https://www.locize.app/register?invitation=jLAFuEOvlBR86IcF1dZFHobsYdINvFoi0dMxxrJHvXIyxnosWcDblcXn6FtOhcOo)**
    - **[Indonesian (id)](https://www.locize.app/register?invitation=S94A2D6glr7w64Nf27XwmvzkcnTRAWhxFoxcl7ZRnmm1dMx23kfpZCx8ROlqb6X2)**
    - **[Icelandic (is)](https://www.locize.app/register?invitation=zgnniaPHGcML0dX5TdZ7Cv9fVkWkVGbrShol4JZYaYQXyk7t2oNlITaImx5n3wZe)**
    - **[Italian (it)](https://www.locize.app/register?invitation=LI6zvAH82797Gro6lc490d34zrw8vthQPdN01n9nfk2c1LW5VVpf4Db6WL5GFHir)**
    - **[Japanese (ja)](https://www.locize.app/register?invitation=VVJLuv7WjjBs0wShxZheCIvwzk7XszVsHCAMZmCQ34SJYrwIeB1GW6u2vkYNJdzk)**
    - **[Georgian (ka)](https://www.locize.app/register?invitation=WK5hB5nznxioaDGOXXPDmcULrNfFA3wkcvS2YiUbrU6b73gKOWix5Qg7uGjpPtkH)**
    - **[Korean (ko)](https://www.locize.app/register?invitation=nbL4LZMwehlTvFKNmNGgTTJt9YsZuuyCMjF9yAc92bVIWEjAc9C1G0ujsEl76dYb)**
    - **[Lithuanian (lt)](https://www.locize.app/register?invitation=mObXVLL1r0IpkldNhVvahfdBSxo1AtqytlBavTU9jAb2M5K1HdbEADfpXpJWkyQK)**
    - **[Latvian (lv)](https://www.locize.app/register?invitation=ey55PwxruCkT1EJ95oluthUzDbXwYJL4pXEd2xQoEHLwleVWND4VRND13a7EsioC)**
    - **[Norwegian Bokmål (nb)](https://www.locize.app/register?invitation=14FVJmxYhd7ZSzjmxkF1XEU0jO9HDG7lpRRiOwVNKi19Ad6hD58NWASD0bN8JgaK)**
    - **[Norwegian Nynorsk (nn)](https://www.locize.app/register?invitation=0upNDr8hxNUkMCAu0Lzo1MQnM0i7VXbli9x2wvygCz0q8sAK2z1AwxkgUDOD4n9R)**
    - **[Dutch (nl)](https://www.locize.app/register?invitation=BQoligTqND5E4jlGmXJZVzkLOpo4pTf9wUyN19zZbgNB38JHaciJ2FnIbsLOXPbe)**
    - **[Polish (pl)](https://www.locize.app/register?invitation=ZPiib1OoPM3OHBUow79I8iQNiFk4SHO7HASO3rHdJpKSlJwA4oxsORR2w4yJaPig)**
    - **[Portuguese (pt-PT)](https://www.locize.app/register?invitation=c11DbWJk7O5TNIKon9leIfIbqARz60URaGB0WFQPT2ym3wxUDR8DgIiOlXNIBz13)**
    - **[Brazilian Portuguese (pt-BR)](https://www.locize.app/register?invitation=99JdsEgKuVR9XhQJWi91Jq0LkqigiSz4EOf4gVGSr5RwZPp6ad4JQZLHVwhumNvB)**
    - **[Russian (ru)](https://www.locize.app/register?invitation=rz4C3pVFdfr5XPPNTsYJvjjnC7vHDVWVOfyUzCf33MxhccdYB7vM7jxcHLacGl14)**
    - **[Slovak (sk)](https://www.locize.app/register?invitation=wQkVqwu4Xr2KzmCgsXmqI7nMjyBzT2EKNFS3P6rNe4Kii3DgQG1i5ozeQeVEBmNd)**
    - **[Slovenian (sl)](https://www.locize.app/register?invitation=NyNTniuVwwvEkKoKoOvAT4nzbVuvxE5X4kVUhBE9KSb3sEwEARQtMTeBCqpuV9dk)**
    - **[Swedish (sv)](https://www.locize.app/register?invitation=RYtYhip5O5ACNCth1cIZpByGnZC1b3JttimEe8mrz5NDyEjVAs1PVcMIQ1in4j7D)**
    - **[Thai (th)](https://www.locize.app/register?invitation=3xMjJPqupRNO2BU7K3WMWhxFtoGNzL97hxKIGjE8Yoa4v7lJj8ZjTy5p5dmcfLjW)**
    - **[Turkish (tr)](https://www.locize.app/register?invitation=x3Ov59Gdrk2b76gn5pSVCwuekDs817YOYElXJn9zCYClPG2XlBORQDRygZmdBH4B)**
    - **[Uyghur (ug)](https://www.locize.app/register?invitation=TeQX9ECX0oqhtkyBswm8BeOqlSaA5dKZaptvaEpYlfBhGBSl8PGIYfdVvPvgQnQF)**
    - **[Ukrainian (uk)](https://www.locize.app/register?invitation=4Z060E9kPjmOqO8BmRSvuIujLydZiRCc0lu90iwQSaCche1tSdFcGOrdlDgPZ2ec)**
    - **[Vietnamese (vi)](https://www.locize.app/register?invitation=rhADX8GuhgQmYrmbHT13YVg2WqMLJpgPdh1OBuujn9GoNUVW6RPipYvC20aH1xcQ)**
    - **[Chinese (Simplified) (zh-Hans)](https://www.locize.app/register?invitation=HXXM2h8SJsBLIPJol3usRct0aPPcr809xzfV4DQHolyEfeSjBwEjChd37vE3ZrRw)**
    - **[Chinese (Traditional) (zh-Hant)](https://www.locize.app/register?invitation=9PWBDcMascIBGG6wwobkVT6cL7p7IncFZVqwIe0e7VZd14MJOAMQGk6IjlvgmA00)**


2. **Fill in Your Details:**
Enter your email, password, and any other required information, then click **Sign Up**.

![Create Account](https://github.com/user-attachments/assets/c1ccbfd9-2131-4020-a4b3-7283bf733828)


### Step 2: Explore the Locize Dashboard

After signing up, you’ll be directed to the Locize dashboard, where you can see an overview of the translation project.

- **Dashboard Overview:**
This page displays the available languages and progress statistics for the project.

![Landing Page](https://github.com/user-attachments/assets/818b3d30-3f5a-48e6-8b36-0be3d0691045)


### Step 3: Select Your Language

1. **Open the Language Dropdown:**
Click the dropdown menu that lists all supported languages.

2. **Choose Your Preferred Language:**
For example, if you want to translate into Dutch, scroll down and select **Dutch**.

![Dropdown with Languages](https://github.com/user-attachments/assets/93f713bd-0008-43bc-ba84-b7730d4cfedf)


### Step 4: Navigate to the Translation Page

After selecting your language, click on the translation progress indicator (for example, "35.61% translated"). This will take you to the page where you can contribute translations.

![Selected Dutch Click on Translation](https://github.com/user-attachments/assets/03322ab5-82ad-4958-9008-5e7e17363ca8)


### Step 5: Contribute Your Translation

1. **Browse the Translation Strings:**
The interface displays a list of translation keys along with their original texts.

2. **Select a String to Translate:**
Click on the string you wish to work on.

3. **Enter Your Translation:**
Type your translated text into the input field provided next to the original text.

4. **Review Your Work:**
Ensure that your translation is accurate and clear.

![Start with Translating](https://github.com/user-attachments/assets/bc3e2a47-c297-476e-945b-f7f0b1356ffb)


### Step 6: Save and Submit Your Translation

1. **Submit Your Translation:**
Once you’re satisfied with your translation, click the **Save** button to submit it for review.

2. **Pending Review:**
Your submitted translation will be marked as pending and will be reviewed by project maintainers.

![Saved Submitted Translation Waiting for Review](https://github.com/user-attachments/assets/a26ae981-0c32-47a0-a296-530ce671375a)


### Step 7: Translation Approval

After review, your translation will be approved and integrated into the project.

- **Approved Translation:**
Once approved, your contribution will be reflected in the Locize dashboard and the overall translation progress.

![Translation Approved](https://github.com/user-attachments/assets/93c3e512-616f-40d9-af2f-9f7c19d53148)

---

## Handling `{{0}}` and `{{1}}` in Translation Strings

Sometimes translation strings need to include dynamic content. These dynamic parts, called **interpolations**, are represented by placeholders enclosed in double curly brackets (e.g., `{{0}}` or `{{1}}`). When translating such strings, it's important to maintain these placeholders in the correct positions.

Below are two examples to help guide you:

### Example 1: Single Interpolation

Consider the translation key `com_assistants_completed_action`. The original English text is:

```text
Talked to {{0}}
```

For the German translation, ensure the placeholder remains intact and is placed appropriately:

```text
Mit {{0}} gesprochen
```

This image shows how a single interpolation is represented in a translation string:

![Single Interpolation](https://github.com/user-attachments/assets/384ef7c1-9b02-490c-8ca4-b7f74943893f)

---

### Example 2: Multiple Interpolations

Now, look at the translation key `com_files_number_selected`, which includes two placeholders. The English version is:

```text
{{0}} of {{1}} item(s) selected
```

In the German translation, both placeholders must be preserved and positioned correctly:

```text
{{0}} von {{1}} Datei(en) ausgewählt
```

This image illustrates how multiple interpolations appear in translation strings:

![Multiple Interpolations](https://github.com/user-attachments/assets/f3376487-e092-442a-b849-b6ab5d5b390d)

---


## Adding a New Language

If you don't see your language listed in our translation table, you can help us expand our language support:

1. **Create a New Issue:**
Open a new issue in our GitHub repository: [LibreChat Issues](https://github.com/danny-avila/LibreChat/issues).

2. **Select the New Language Request Template:**
Use the **New Language Request** template and provide:
 - The full name of your language (e.g., Spanish, Mandarin).
 - The [ISO 639-1](https://www.w3schools.com/tags/ref_language_codes.asp) code for your language (e.g., `es` for Spanish).

3. **Collaborate with Maintainers:**
Our maintainers will review your request and work with you to integrate the new language. Once approved, your language will appear in the translation progress table, and you can start contributing.

---

## Need Help?

If you have any questions or need assistance, please feel free to:

- **Open an Issue:**
Submit an issue in our repository: [LibreChat Issues](https://github.com/danny-avila/LibreChat/issues).

- **Join Our Discord Community:**
Connect with fellow translators on our [Discord server](https://discord.librechat.ai).

- **Contact a Maintainer:**
Reach out directly to one of our project maintainers for additional support.

Your contributions help make LibreChat accessible to users worldwide. Thank you for supporting our project, and happy translating!


---

We thank [Locize](https://locize.com) for their translation management tools that support multiple languages in LibreChat.

<p align="center">
    <a href="https://locize.com" target="_blank" rel="noopener noreferrer">
        <img src="https://github.com/user-attachments/assets/d6b70894-6064-475e-bb65-92a9e23e0077" alt="Locize Logo" height="50"></img>
    </a>
</p>

# Overview (https://www.librechat.ai/docs/development)

Docker is the preferred install path for most users, but local LibreChat development should use
`npm`. Running the app directly on your machine gives faster feedback, clearer debugging, and direct
access to the monorepo workspaces without rebuilding containers after each change.

## Recommended Toolchain

Use this toolchain for npm-based development:

| Tool    | Version                   |
| ------- | ------------------------- |
| Node.js | `v24.16.0`                |
| npm     | `v11.16.0`                |
| MongoDB | Atlas or Community Server |

Node 24 satisfies LibreChat's runtime needs for CommonJS interop with ESM-only packages, WebCrypto,
and the Fetch API. If your shell still reports an older Node version, run `nvm use 24.16.0` from the
LibreChat repository before installing dependencies.

## Work In The Right Workspace

LibreChat is a monorepo. Pick the smallest workspace that owns the behavior you are changing:

| Workspace                 | Use it for                                                            |
| ------------------------- | --------------------------------------------------------------------- |
| `/packages/api`           | New backend TypeScript services, controllers, and shared server logic |
| `/api`                    | Legacy Express server integration; keep changes thin                  |
| `/packages/data-schemas`  | Database models, schemas, and database-specific shared logic          |
| `/packages/data-provider` | Shared API types, endpoints, query keys, and data-service functions   |
| `/client`                 | React application code                                                |
| `/packages/client`        | Shared frontend utilities                                             |

## Daily Commands

| Command                       | Purpose                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------- |
| `npm run smart-reinstall`     | Install dependencies when needed and build compiled workspaces                     |
| `npm run reinstall`           | Clean install after changing Node/npm versions or when dependency state is suspect |
| `npm run backend:dev`         | Start the backend with file watching                                               |
| `npm run frontend:dev`        | Start the frontend dev server on port `3090`                                       |
| `npm run build:data-provider` | Rebuild shared data-provider code after API/type changes                           |
| `npm run build`               | Build all compiled workspaces through Turborepo                                    |

## Development Resources

<Callout type="info" title="Development Resources">
  - If you are new to repositories, forks, branches, and pull requests, start with **[GitHub's collaborative development guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models)**.
  - Read the **[Contributor Guidelines](https://github.com/danny-avila/LibreChat/blob/main/.github/CONTRIBUTING.md)** before opening a PR.
  - Use **[Contributor Setup](/docs/development/get_started)** for the full local setup flow.
  - Use **[Code Standards and Conventions](/docs/development/conventions)** for workspace boundaries, import order, typing, testing, and frontend rules.
</Callout>


# Contributor Setup (https://www.librechat.ai/docs/development/get_started)

## Requirements

- [Git](https://git-scm.com/downloads) (Essential)
- [Node.js](https://nodejs.org/en/download) `v24.16.0` (Essential)
- npm `v11.16.0` (Essential)
- [MongoDB](https://www.mongodb.com/try/download/community) (Essential, for the database)
- [Git LFS](https://git-lfs.com/) (Useful for larger files)
- [GitHub Desktop](https://desktop.github.com/) (Optional)
- [VSCode](https://code.visualstudio.com/Download) (Recommended Source-code Editor)

### Recommended VSCode Extensions

Install these extensions in VS Code:

- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)

## Prepare the Environment

### Node.js and npm

If you use `nvm`, install and select the recommended Node.js version before installing LibreChat:

```sh filename="Use Node.js 24"
nvm install 24.16.0
nvm use 24.16.0
npm install -g npm@11.16.0
```

Verify your shell is using the expected versions:

```sh filename="Check Node.js and npm"
node -v
npm -v
```

```txt filename="Expected versions"
v24.16.0
11.16.0
```

### GitHub

- Fork the LibreChat repository: [https://github.com/danny-avila/LibreChat/fork](https://github.com/danny-avila/LibreChat/fork)

- Create a branch on your fork, name it properly, and link it to the original repository.

- Download your new branch to your local PC

```sh filename="Download your LibreChat branch"
git clone -b branch-name https://github.com/username/LibreChat.git
```

> Replace `branch-name` and `username` with your details

### Open in VS Code

- After cloning your branch:
  ```sh filename="Navigate to LibreChat folder"
  cd LibreChat
  ```
  ```sh filename="Open in VS Code"
  code .
  ```

### Prepare LibreChat

- Open the terminal in VS Code with `ctrl`+`shift`+`` ```

  > Alternatively, use `ctrl`+`j` to open the bottom pane and select the terminal.

- ```sh filename="Install LibreChat dependencies"
  npm run smart-reinstall
  ```

  > If you just changed Node.js or npm versions, use `npm run reinstall` once for a clean install.

- ```sh filename="Build all compiled code"
  npm run build
  ```

- .env Configuration
  - Create the `.env` file. If you don't have one, duplicate `.env.example` and configure it.

<Callout type="warning" title="Warning">
  The default values in `.env.example` are usually fine, except for `MONGO_URI`. Provide your own.
  Make sure to install MongoDB and configure `MONGO_URI` correctly to connect to your MongoDB
  instance. Use [MongoDB Community Server](https://www.mongodb.com/try/download/community) or
  [MongoDB Atlas Cloud](https://www.mongodb.com/cloud/atlas/register).
</Callout>

### Development Workflow

For efficient work on LibreChat, use these commands:

- **Starting Backend:**
  - Use `npm run backend` for normal operation.
  - For active development, use `npm run backend:dev` to monitor changes.
  - Access at `http://localhost:3080/`.

- **Running Frontend in Development Mode:**
  - **Ensure backend is running.**
  - Use `npm run frontend:dev` to monitor frontend changes.
  - View at `http://localhost:3090/`.

<Callout type="tip" title="Pro Tips">
  - For real-time updates during frontend development, run `npm run frontend:dev` so frontend changes refresh on port `3090`.
  - Set `DEBUG_CONSOLE=true` in `.env` for verbose server output in the console.
</Callout>

## Local Testing

Before submission, test your updates locally, see: [Perform Tests Locally](/docs/development/testing)

By running tests, ensure your contributions are robust and ready for integration.

## Commit, Push, Pull Request (PR)

### Make a Commit

**Commits** mark logical checkpoints in development. Include clear messages explaining changes.

**Example:**

```bash filename=" "
git add .
git commit -m "Add login functionality"
```

### Push Changes

**Push** changes to the remote repository after completing a feature or fixing an issue.

**Example:**

```bash filename=" "
git push origin feature-branch-name
```

### Create a Pull Request (PR)

**Pull Request** merges changes from a feature branch into the main branch.

1. Pull latest changes from main branch and resolve conflicts.
2. Push updated feature branch.
3. Ensure code follows project guidelines.

**Example:**

```bash filename=" "
git checkout main
git pull origin main
git checkout feature-branch-name
git rebase main
# Resolve conflicts if any
git push origin feature-branch-name
# Open PR on GitHub
```

Access your repository in a browser and click "Contribute".

<Callout type="info" title="Note:">
  Provide a detailed PR description explaining changes and their value. Reference related issues.
</Callout>

<Callout type="tip" title="Tip">
  Use GitHub Desktop to track changes.
</Callout>

<Callout type="warning" title="Warning">
  If `git commit` fails due to ESLint errors, understand and fix the issue.
</Callout>

## Revert Commits Safely

To undo changes in a feature branch, follow these steps cautiously:

- ```bash filename="1. Update local repository from feature branch:"
  git pull origin feature-branch-name
  ```

- ```bash filename="2. Review commit history to determine commits to revert:"
  git log
  ```

- ```bash filename="3. Start an interactive rebase for 'N' commits to revert:"
  git rebase -i HEAD~N
  ```

  > Replace `pick` with `drop` for commits to remove. Save and exit editor.

- ```bash filename="4. Force push changes to remote repository:"
  git push --force-with-lease origin feature-branch-name
  ```


# Tools and Plugins (https://www.librechat.ai/docs/development/tools_and_plugins)

**This page is deprecated. Please refer to the [Agents Guide](/docs/features/agents) for the most up-to-date information on using tools.**

**It is highly recommend to use the [Model Context Protocol](/docs/features/agents#model-context-protocol-mcp) or [OpenAPI Actions](/docs/features/agents#actions) for integrating custom tools**

# Making your own Tools/Plugins

<Callout type="warning" title="Warning">
  Please refer to the most recents tools used with assistants in `api/app/clients/tools/structured/`
  since plugins will be deprecated in favor of tools in the near future
</Callout>

Creating custom plugins for this project involves extending the `Tool` class from the `langchain/tools` module.

**Note:** I will use the word plugin interchangeably with tool, as the latter is specific to LangChain, and we are mainly conforming to the library.

You are essentially creating DynamicTools in LangChain speak. See the **[LangChainJS docs](https://js.langchain.com/docs/how_to/custom_tools/)** for more info.

This guide will walk you through the process of creating your own custom plugins, using the `StableDiffusionAPI` and `WolframAlphaAPI` tools as examples.

When using the Functions Agent (the default mode for plugins), tools are converted to **[OpenAI functions](https://openai.com/blog/function-calling-and-other-api-updates)**; in any case, plugins/tools are invoked conditionally based on the LLM generating a specific format that we parse.

The most common implementation of a plugin is to make an API call based on the natural language input from the AI, but there is virtually no limit in programmatic use case.

---

## Key Takeaways

Here are the key takeaways for creating your own plugin:

**1.** [**Import Required Modules:**](#step-1-import-required-modules) Import the necessary modules for your plugin, including the `Tool` class from `langchain/tools` and any other modules your plugin might need.

**2.** [**Define Your Plugin Class:**](#step-2-define-your-tool-class) Define a class for your plugin that extends the `Tool` class. Set the `name` and `description` properties in the constructor. If your plugin requires credentials or other variables, set them from the fields parameter or from a method that retrieves them from your process environment. Note: if your plugin requires long, detailed instructions, you can add a `description_for_model` property and make `description` more general.

**3.** [**Define Helper Methods:**](#step-3-define-helper-methods) Define helper methods within your class to handle specific tasks if needed.

**4.** [**Implement the `_call` Method:**](#step-4-implement-the-_call-method) Implement the `_call` method where the main functionality of your plugin is defined. This method is called when the language model decides to use your plugin. It should take an `input` parameter and return a result. If an error occurs, the function should return a string representing an error, rather than throwing an error. If your plugin requires multiple inputs from the LLM, read the [StructuredTools](#StructuredTools) section.

**5.** [**Export Your Plugin and Import into handleTools.js:**](#step-5-export-your-plugin-and-import-into-handletoolsjs) Export your plugin and import it into `handleTools.js`. Add your plugin to the `toolConstructors` object in the `loadTools` function. If your plugin requires more advanced initialization, add it to the `customConstructors` object.

**6.** [**Export Your Plugin into index.js:**](#step-6-export-your-plugin-into-indexjs) Export your plugin into `index.js` under `tools`. Add your plugin to the `module.exports` of the `index.js`, so you also need to declare it as `const` in this file.

**7.** [**Add Your Plugin to manifest.json:**](#step-7-add-your-plugin-to-manifestjson) Add your plugin to `manifest.json`. Follow the strict format for each of the fields of the "plugin" object. If your plugin requires authentication, add those details under `authConfig` as an array. The `pluginKey` should match the class `name` of the Tool class you made, and the `authField` prop must match the process.env variable name.

Remember, the key to creating a custom plugin is to extend the `Tool` class and implement the `_call` method. The `_call` method is where you define what your plugin does. You can also define helper methods and properties in your class to support the functionality of your plugin.

**Note: You can find all the files mentioned in this guide in the `.\api\app\langchain\tools` folder.**

---

## StructuredTools

**Multi-Input Plugins**

If you would like to make a plugin that would benefit from multiple inputs from the LLM, instead of a singular input string as we will review, you need to make a LangChain **[StructuredTool](https://blog.langchain.dev/structured-tools/)** instead. A detailed guide for this is in progress, but for now, you can look at how I've made StructuredTools in this directory: `api\app\clients\tools\structured\`. This guide is foundational to understanding StructuredTools, and it's recommended you continue reading to better understand LangChain tools first. The blog linked above is also helpful once you've read through this guide.

---

## Step 1: Import Required Modules

Start by importing the necessary modules. This will include the `Tool` class from `langchain/tools` and any other modules your tool might need. For example:

```javascript
const { Tool } = require('langchain/tools')
// ... whatever else you need
```

## Step 2: Define Your Tool Class

Next, define a class for your plugin that extends the `Tool` class. The class should have a constructor that calls the `super()` method and sets the `name` and `description` properties. These properties will be used by the language model to determine when to call your tool and with what parameters.

**Important:** you should set credentials/necessary variables from the fields parameter, or alternatively from a method that gets it from your process environment

```javascript
class StableDiffusionAPI extends Tool {
  constructor(fields) {
    super();
    this.name = 'stable-diffusion';
    this.url = fields.SD_WEBUI_URL || this.getServerURL(); // <--- important!
    this.description = `You can generate images with 'stable-diffusion'. This tool is exclusively for visual content...`;
  }
  ...
}
```

**Optional:** As of v0.5.8, when using Functions, you can add longer, more detailed instructions, with the `description_for_model` property. When doing so, it's recommended you make the `description` property more generalized to optimize tokens. Each line in this property is prefixed with `// ` to mirror how the prompt is generated for ChatGPT (chat.openai.com). This format more closely aligns to the prompt engineering of official ChatGPT plugins.

```js
// ...
this.description_for_model = `// Generate images and visuals using text with 'stable-diffusion'.
// Guidelines:
// - ALWAYS use {{"prompt": "7+ detailed keywords", "negative_prompt": "7+ detailed keywords"}} structure for queries.
// - Visually describe the moods, details, structures, styles, and/or proportions of the image. Remember, the focus is on visual attributes.
// - Craft your input by "showing" and not "telling" the imagery. Think in terms of what you'd want to see in a photograph or a painting.
// - Here's an example for generating a realistic portrait photo of a man:
// "prompt":"photo of a man in black clothes, half body, high detailed skin, coastline, overcast weather, wind, waves, 8k uhd, dslr, soft lighting, high quality, film grain, Fujifilm XT3"
// "negative_prompt":"semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime, out of frame, low quality, ugly, mutation, deformed"
// - Generate images only once per human query unless explicitly requested by the user`
this.description =
  "You can generate images using text with 'stable-diffusion'. This tool is exclusively for visual content."
// ...
```

Within the constructor, note that we're getting a sensitive variable from either the fields object or from the **getServerURL** method we define to access an environment variable.

```js
this.url = fields.SD_WEBUI_URL || this.getServerURL()
```

Any credentials necessary are passed through `fields` when the user provides it from the frontend; otherwise, the admin can "authorize" the plugin for all users through environment variables. All credentials passed from the frontend are encrypted.

```js
// It's recommended you follow this convention when accessing environment variables.
  getServerURL() {
    const url = process.env.SD_WEBUI_URL || '';
    if (!url) {
      throw new Error('Missing SD_WEBUI_URL environment variable.');
    }
    return url;
  }
```

## Step 3: Define Helper Methods

You can define helper methods within your class to handle specific tasks if needed. For example, the `StableDiffusionAPI` class includes methods like `replaceNewLinesWithSpaces`, `getMarkdownImageUrl`, and `getServerURL` to handle various tasks.

```javascript
class StableDiffusionAPI extends Tool {
  ...
  replaceNewLinesWithSpaces(inputString) {
    return inputString.replace(/\r\n|\r|\n/g, ' ');
  }
  ...
}
```

## Step 4: Implement the `_call` Method

The `_call` method is where the main functionality of your plugin is implemented. This method is called when the language model decides to use your plugin. It should take an `input` parameter and return a result.

> In a basic Tool, the LLM will generate one string value as an input. If your plugin requires multiple inputs from the LLM, read the **[StructuredTools](#StructuredTools)** section.

```javascript
class StableDiffusionAPI extends Tool {
  ...
  async _call(input) {
    // Your tool's functionality goes here
    ...
    return this.result;
  }
}
```

**Important:** The \_call function is what will the agent will actually call. When an error occurs, the function should, when possible, return a string representing an error, rather than throwing an error. This allows the error to be passed to the LLM and the LLM can decide how to handle it. If an error is thrown, then execution of the agent will stop.

## Step 5: Export Your Plugin and import into handleTools.js

**This process will be somewhat automated in the future, as long as you have your plugin/tool in `api\app\langchain\tools`**

```javascript
// Export
module.exports = StableDiffusionAPI
```

```js
/* api\app\langchain\tools\handleTools.js */
const StableDiffusionAPI = require('./StableDiffusion');
...
```

In handleTools.js, find the beginning of the `loadTools` function and add your plugin/tool to the toolConstructors object.

```js
const loadTools = async ({ user, model, tools = [], options = {} }) => {
  const toolConstructors = {
    calculator: Calculator,
    google: GoogleSearchAPI,
    wolfram: WolframAlphaAPI,
    'dall-e': OpenAICreateImage,
    'stable-diffusion': StableDiffusionAPI // <----- Newly Added. Note: the key is the 'name' provided in the class.
    // We will now refer to this name as the `pluginKey`
  };
```

If your Tool class requires more advanced initialization, you would add it to the customConstructors object.

The default initialization can be seen in the `loadToolWithAuth` function, and most custom plugins should be initialized this way.

Here are a few customConstructors, which have varying initializations

```javascript
const customConstructors = {
  browser: async () => {
    let openAIApiKey = process.env.OPENAI_API_KEY
    if (!openAIApiKey) {
      openAIApiKey = await getUserPluginAuthValue(user, 'OPENAI_API_KEY')
    }
    return new WebBrowser({ model, embeddings: new OpenAIEmbeddings({ openAIApiKey }) })
  },
  // ...
  plugins: async () => {
    return [
      new HttpRequestTool(),
      await AIPluginTool.fromPluginUrl(
        'https://www.klarna.com/.well-known/ai-plugin.json',
        new ChatOpenAI({ openAIApiKey: options.openAIApiKey, temperature: 0 }),
      ),
    ]
  },
}
```

## Step 6: Export your Plugin into index.js

Find the `index.js` under `api/app/clients/tools`. You need to put your plugin into the `module.exports`, to make it compile, you will also need to declare your plugin as `consts`:

```js
const StructuredSD = require('./structured/StableDiffusion');
const StableDiffusionAPI = require('./StableDiffusion');
...
module.exports = {
  ...
  StableDiffusionAPI,
  StructuredSD,
  ...
}
```

## Step 7: Add your Plugin to manifest.json

**This process will be somehwat automated in the future along with step 5, as long as you have your plugin/tool in `api\app\langchain\tools`, and your plugin can be initialized with the default method**

```json
  {
    "name": "Calculator",
    "pluginKey": "calculator",
    "description": "Perform simple and complex mathematical calculations.",
    "icon": "https://i.imgur.com/RHsSG5h.png",
    "isAuthRequired": "false",
    "authConfig": []
  },
  {
    "name": "Stable Diffusion",
    "pluginKey": "stable-diffusion",
    "description": "Generate photo-realistic images given any text input.",
    "icon": "https://i.imgur.com/Yr466dp.png",
    "authConfig": [
      {
        "authField": "SD_WEBUI_URL",
        "label": "Your Stable Diffusion WebUI API URL",
        "description": "You need to provide the URL of your Stable Diffusion WebUI API. For instructions on how to obtain this, see <a href='url'>Our Docs</a>."
      }
    ]
  },
```

Each of the fields of the "plugin" object are important. Follow this format strictly. If your plugin requires authentication, you will add those details under `authConfig` as an array since there could be multiple authentication variables. See the Calculator plugin for an example of one that doesn't require authentication, where the authConfig is an empty array (an array is always required).

**Note:** as mentioned earlier, the `pluginKey` matches the class `name` of the Tool class you made.
**Note:** the `authField` prop must match the process.env variable name
**Note:** `authConfig` entries can include `sensitive`. Omit it or set it to `true` for API keys and secrets. Set `sensitive: false` for non-secret setup values such as URLs, usernames, deployment names, or project IDs so the UI renders a plain text field instead of a secret input.

Here is an example of a plugin with more than one credential variable

```json
  [
  {
    "name": "Google",
    "pluginKey": "google",
    "description": "Use Google Search to find information about the weather, news, sports, and more.",
    "icon": "https://i.imgur.com/SMmVkNB.png",
    "authConfig": [
      {
        "authField": "GOOGLE_CSE_ID",
        "label": "Google CSE ID",
        "description": "This is your Google Custom Search Engine ID. For instructions on how to obtain this, see <a href='https://github.com/danny-avila/LibreChat/blob/main/docs/features/plugins/google_search.md'>Our Docs</a>.",
        "sensitive": false
      },
      {
        "authField": "GOOGLE_SEARCH_API_KEY",
        "label": "Google API Key",
        "description": "This is your Google Custom Search API Key. For instructions on how to obtain this, see <a href='https://github.com/danny-avila/LibreChat/blob/main/docs/features/plugins/google_search.md'>Our Docs</a>.",
        "sensitive": true
      }
    ]
  },
```

## Example: WolframAlphaAPI Tool

Here's another example of a custom tool, the `WolframAlphaAPI` tool. This tool uses the `axios` module to make HTTP requests to the Wolfram Alpha API.

```javascript
const axios = require('axios')
const { Tool } = require('langchain/tools')

class WolframAlphaAPI extends Tool {
  constructor(fields) {
    super()
    this.name = 'wolfram'
    this.apiKey = fields.WOLFRAM_APP_ID || this.getAppId()
    this.description = `Access computation, math, curated knowledge & real-time data through wolframAlpha...`
  }

  async fetchRawText(url) {
    try {
      const response = await axios.get(url, { responseType: 'text' })
      return response.data
    } catch (error) {
      console.error(`Error fetching raw text: ${error}`)
      throw error
    }
  }

  getAppId() {
    const appId = process.env.WOLFRAM_APP_ID || ''
    if (!appId) {
      throw new Error('Missing WOLFRAM_APP_ID environment variable.')
    }
    return appId
  }

  createWolframAlphaURL(query) {
    const formattedQuery = query.replaceAll(/`/g, '').replaceAll(/\n/g, ' ')
    const baseURL = 'https://www.wolframalpha.com/api/v1/llm-api'
    const encodedQuery = encodeURIComponent(formattedQuery)
    const appId = this.apiKey || this.getAppId()
    const url = `${baseURL}?input=${encodedQuery}&appid=${appId}`
    return url
  }

  async _call(input) {
    try {
      const url = this.createWolframAlphaURL(input)
      const response = await this.fetchRawText(url)
      return response
    } catch (error) {
      if (error.response && error.response.data) {
        console.log('Error data:', error.response.data)
        return error.response.data
      } else {
        console.log(`Error querying Wolfram Alpha`, error.message)
        return 'There was an error querying Wolfram Alpha.'
      }
    }
  }
}

module.exports = WolframAlphaAPI
```

In this example, the `WolframAlphaAPI` class has helper methods like `fetchRawText`, `getAppId`, and `createWolframAlphaURL` to handle specific tasks. The `_call` method makes an HTTP request to the Wolfram Alpha API and returns the response.


# Testing During Development (https://www.librechat.ai/docs/development/testing)

## Local Unit Tests

Before submitting your updates, verify they pass all unit tests. Follow these steps to run tests locally:

- Copy your `.env.example` file in the `/api` folder and rename it to `.env`

  ```bash filename="create a /api/.env file"
  cp .env.example ./api/.env
  ```

- Add `NODE_ENV=CI` to your `/api/.env` file
- `npm run test:client`
- `npm run test:api`
- `npm run test:packages:api`
- `npm run test:packages:data-provider`
- `npm run test:packages:data-schemas`

### Running Tests Per-Workspace

Tests are run using Jest from their respective workspace directories. Target specific test files with patterns:

```bash
cd api && npx jest <pattern>
cd packages/api && npx jest <pattern>
cd packages/data-provider && npx jest <pattern>
cd packages/data-schemas && npx jest <pattern>
cd client && npx jest <pattern>
```

## Testing Philosophy

- Prefer real logic over mocks. Mock only what cannot be controlled locally, such as external HTTP
  APIs, rate-limited services, and non-deterministic system calls.
- Use spies when you need to assert that real functions were called with expected arguments.
- Use `mongodb-memory-server` for MongoDB-backed tests so queries and schema validation run against
  real database behavior.
- Cover loading, success, and error states for UI/data flows.

<Callout type="tip" title="Tip">
  Use `test/layout-test-utils` for rendering components in frontend tests.
</Callout>


# Debugging (https://www.librechat.ai/docs/development/debugging)

<Callout type="warning" title="Under construction, contributions are welcome!" />

see also: [Logging System](/docs/configuration/logging)

# Project Architecture (https://www.librechat.ai/docs/development/architecture)

## Monorepo Structure

LibreChat is organized as a monorepo with clearly defined workspace boundaries:

| Workspace | Language | Side | Dependency | Purpose |
|---|---|---|---|---|
| `/api` | JS (legacy) | Backend | `packages/api`, `packages/data-schemas`, `packages/data-provider`, `@librechat/agents` | Express server — minimize changes here |
| `/packages/api` | **TypeScript** | Backend | `packages/data-schemas`, `packages/data-provider` | New backend code lives here (TS only, consumed by `/api`) |
| `/packages/data-schemas` | TypeScript | Backend | `packages/data-provider` | Database models/schemas, shareable across backend projects |
| `/packages/data-provider` | TypeScript | Shared | — | Shared API types, endpoints, data-service — used by both frontend and backend |
| `/client` | TypeScript/React | Frontend | `packages/data-provider`, `packages/client` | Frontend SPA |
| `/packages/client` | TypeScript | Frontend | `packages/data-provider` | Shared frontend utilities |

### Key Principles

- **All new backend code must be TypeScript** in `/packages/api`.
- Keep `/api` changes to the absolute minimum — thin JS wrappers calling into `/packages/api`.
- Database-specific shared logic belongs in `/packages/data-schemas`.
- Frontend/backend shared API logic (endpoints, types, data-service) belongs in `/packages/data-provider`.

### Build and Install

| Command | Purpose |
|---|---|
| `npm run smart-reinstall` | Install deps (if lockfile changed) + build via Turborepo |
| `npm run reinstall` | Clean install after changing Node/npm versions or when dependency state is suspect |
| `npm run build` | Build all compiled code via Turborepo (parallel, cached) |
| `npm run frontend` | Build all compiled code sequentially (legacy fallback) |
| `npm run build:data-provider` | Rebuild `packages/data-provider` after changes |
| `npm run backend` | Start the backend server |
| `npm run backend:dev` | Start backend with file watching (development) |
| `npm run frontend:dev` | Start frontend dev server with HMR (port 3090, requires backend running) |

- Node.js: `v24.16.0`
- npm: `v11.16.0`
- Database: MongoDB
- Backend runs on `http://localhost:3080/`; frontend dev server on `http://localhost:3090/`

<Callout type="info" title="Note">
  For the full set of coding standards and conventions, see [Code Standards and Conventions](/docs/development/conventions).
</Callout>


# Code Standards and Conventions (https://www.librechat.ai/docs/development/conventions)

## Workspace Boundaries

LibreChat is a monorepo. All new code should target the correct workspace:

| Workspace | Language | Side | Purpose |
|---|---|---|---|
| `/api` | JS (legacy) | Backend | Express server — minimize changes here |
| `/packages/api` | **TypeScript** | Backend | New backend code lives here (TS only, consumed by `/api`) |
| `/packages/data-schemas` | TypeScript | Backend | Database models/schemas and database-specific shared logic |
| `/packages/data-provider` | TypeScript | Shared | API types, endpoints, data-service — used by frontend and backend |
| `/client` | TypeScript/React | Frontend | Frontend SPA |
| `/packages/client` | TypeScript | Frontend | Shared frontend utilities |

- **All new backend code must be TypeScript** in `/packages/api`.
- Keep `/api` changes to the absolute minimum (thin JS wrappers calling into `/packages/api`).
- Database-specific shared logic goes in `/packages/data-schemas`.
- Frontend/backend shared API logic (endpoints, types, data-service) goes in `/packages/data-provider`.
- Build all compiled code from project root: `npm run build`.
- Rebuild shared data-provider code after API/type changes: `npm run build:data-provider`.

---

## General Guidelines

- Use "clean code" principles: keep functions and modules small, adhere to the single responsibility principle, and write expressive and readable code.
- Use meaningful and descriptive variable and function names.
- Prioritize code readability and maintainability over brevity.
- Use the provided `.eslintrc` and `.prettierrc` files for consistent code formatting.
- Fix all formatting lint errors using auto-fix when available. All TypeScript/ESLint warnings and errors must be resolved.

### Naming and File Organization

- Use single-word file names whenever possible, such as `permissions.ts`, `capabilities.ts`,
  or `service.ts`.
- When multiple words are needed, prefer a single-word directory that gives the file context, such
  as `admin/capabilities.ts` instead of `adminCapabilities.ts`.
- Let the directory provide context. Prefer `app/service.ts` over `app/appConfigService.ts`.

### Code Structure

- **Never-nesting**: use early returns, flat code, minimal indentation. Break complex operations into well-named helpers.
- **Functional first**: pure functions, immutable data, `map`/`filter`/`reduce` over imperative loops. Only reach for OOP when it clearly improves domain modeling or state encapsulation.
- **No dynamic imports** unless absolutely necessary.
- Extract repeated logic into dedicated utility functions (DRY). Prefer parameterized helpers,
  constants, shared validators, centralized error handling, and shared types over near-duplicate
  implementations.

### Iteration and Performance

- **Minimize looping** — especially over shared data structures like message arrays, which are iterated frequently. Every additional pass adds up at scale.
- Consolidate sequential O(n) operations into a single pass whenever possible; never loop over the same collection twice if the work can be combined.
- Choose data structures that reduce the need to iterate (e.g., `Map`/`Set` for lookups instead of `Array.find`/`Array.includes`).
- Avoid unnecessary object creation; consider space-time tradeoffs.
- Prevent memory leaks: be careful with closures, dispose resources/event listeners, avoid circular references.

### Type Safety

- **Never use `any`**. Explicit types for all parameters, return values, and variables.
- **Limit `unknown`** — avoid `unknown`, `Record<string, unknown>`, and `as unknown as T` assertions. A `Record<string, unknown>` almost always signals a missing explicit type definition.
- **Don't duplicate types** — check whether a type already exists in the project (especially `packages/data-provider`) before defining a new one. Reuse and extend existing types.
- Use union types, generics, and interfaces appropriately.

### Comments and Documentation

- Write self-documenting code; no inline comments narrating what code does.
- JSDoc only for complex/non-obvious logic or intellisense on public APIs.
- Single-line JSDoc for brief docs, multi-line for complex cases.
- Avoid standalone `//` comments unless absolutely necessary.

### Import Order

Imports are organized into three sections (in order):

1. **Package imports** — sorted from shortest to longest line length (`react` is always the first import).
2. **`import type` imports** — sorted from longest to shortest (package types first, then local types; length sorting resets between sub-groups).
3. **Local/project imports** — sorted from longest to shortest.

- Consolidate value imports from the same module as much as possible.
- Always use standalone `import type { ... }` for type imports; never use inline `type` keyword inside value imports (e.g., `import { Foo, type Bar }` is wrong).

### Loop Preferences

- **Limit looping as much as possible.** Prefer single-pass transformations and avoid re-iterating the same data.
- `for (let i = 0; ...)` for performance-critical or index-dependent operations.
- `for...of` for simple array iteration.
- `for...in` only for object property enumeration.

---

## Node.js API Server

### API Design

- Follow RESTful principles when designing APIs.
- Use meaningful and descriptive names for routes, controllers, services, and models.
- Use appropriate HTTP methods (GET, POST, PUT, DELETE) for each route.
- Use proper status codes and response structures for consistent API responses (2xx for success, 4xx for bad request from client, 5xx for server error).
- Use try-catch blocks to catch and handle exceptions gracefully.
- Implement proper error handling and consistently return appropriate error responses.
- Use the logging system included in the `utils` directory to log important events and errors.
- Do JWT-based, stateless authentication using the `requireJWTAuth` middleware.

### File Structure

New backend code goes in `/packages/api` as TypeScript. The legacy `/api` directory follows this structure:

#### Routes

Specifies each HTTP request method, any middleware to be used, and the controller function to be called for each route.

- Define routes using the Express Router in separate files for each resource or logical grouping.
- Use descriptive route names and adhere to RESTful conventions.
- Keep routes concise and focused on a single responsibility.
- Prefix all routes with the `/api` namespace.

#### Controllers

Contains the logic for each route, including calling the appropriate service functions and returning the appropriate response status code and JSON body.

- Create a separate controller file for each route to handle the request/response logic.
- Name controller files using the PascalCase convention and append "Controller" to the file name (e.g., `UserController.js`).
- Keep controllers thin by delegating complex operations to service or model files.

#### Services

Contains complex business logic or operations shared across multiple controllers.

- Name service files using the PascalCase convention and append "Service" to the file name (e.g., `AuthService.js`).
- Avoid tightly coupling services to specific models or databases for better reusability.
- Maintain a single responsibility principle within each service.

#### Models

Defines Mongoose models to represent data entities and their relationships.

- Use singular, PascalCase names for model files and their associated collections (e.g., `User.js` and `users` collection).
- Include only the necessary fields, indexes, and validations in the models.
- Keep models independent of the API layer by avoiding direct references to request/response objects.

### Database Access (MongoDB and Mongoose)

- Use Mongoose ([https://mongoosejs.com](https://mongoosejs.com)) as the MongoDB ODM.
- Create separate model files for each entity and ensure clear separation of concerns.
- Use Mongoose schema validation to enforce data integrity.
- Handle database connections efficiently and avoid connection leaks.
- Use Mongoose query builders to create concise and readable database queries.

---

## React Client

### General TypeScript and React Best Practices

- Use [TypeScript best practices](https://onesignal.com/blog/effective-typescript-for-react-applications/) to benefit from static typing and improved tooling.
- Group related files together within feature directories (e.g., `SidePanel/Memories/`).
- Name components using the PascalCase convention.
- Use concise and descriptive names that accurately reflect the component's purpose.
- Split complex components into smaller, reusable ones when appropriate.
- Keep the rendering logic within components minimal.
- Extract reusable parts into separate functions or hooks.
- Apply prop type definitions using TypeScript types or interfaces.
- Use form validation where appropriate (we use [React Hook Form](https://react-hook-form.com/) for form validation and submission).

### Localization

- All client-facing text must be localized using the `useLocalize()` hook.
- Only update English keys in `client/src/locales/en/translation.json` (other languages are automated externally).
- Use semantic localization key prefixes: `com_ui_`, `com_assistants_`, etc.
- Always provide meaningful fallback text for new localization keys.

### Data Services

- Create data provider hooks in `client/src/data-provider/[Feature]/queries.ts`.
- Export all hooks from `client/src/data-provider/[Feature]/index.ts`.
- Add feature exports to main `client/src/data-provider/index.ts`.
- Use React Query (`@tanstack/react-query`) for all API interactions.
- Implement proper query invalidation on mutations.
- Add QueryKeys and MutationKeys to `packages/data-provider/src/keys.ts`.

When adding shared API integration, update:

- `packages/data-provider/src/api-endpoints.ts` (endpoints)
- `packages/data-provider/src/data-service.ts` (data service functions)
- `packages/data-provider/src/types/queries.ts` (TypeScript types)

### Performance

- Prioritize memory and speed efficiency at scale.
- Implement proper cursor pagination for large datasets.
- Avoid unnecessary re-renders with proper dependency arrays.
- Leverage React Query's caching and background refetching features.

---

## Testing and Documentation

- Write unit tests for all critical and complex functionalities using Jest.
- Write integration tests for all API endpoints using Supertest.
- Write end-to-end tests for all client-side functionalities using Playwright.
- Use descriptive test case and function names to clearly express the test's purpose.
- Run tests from their workspace directory: `cd api && npx jest <pattern>`, `cd packages/api && npx jest <pattern>`, etc.
- Cover loading, success, and error states for UI/data flows.
- Use `test/layout-test-utils` for rendering components in frontend tests.
- Prefer real logic over mocks. Mock only what cannot be controlled locally, such as external HTTP
  APIs, rate-limited services, and non-deterministic system calls.
- Use spies when you need to assert calls without replacing the underlying implementation.
- Use `mongodb-memory-server` for MongoDB-backed tests so queries and schema validation exercise
  real database behavior.
