# Dify — User Guide

Dify is an open-source platform for building LLM-powered applications. It provides a visual workflow editor, RAG (Retrieval-Augmented Generation) pipeline, agent framework, model management, and observability — all through a browser-based interface. You can build chatbots, text generators, workflow automations, and AI agents without writing code, then expose them via API or embeddable web apps.

> [!TIP]
> **New to Dify?** Jump to [Quick Start](#quick-start) for a 5-minute Docker setup, or [Installation](#installation) for production deployments. The [Building Applications](#building-applications) section walks through creating each app type.

---

## Table of Contents

- [Quick Start](#quick-start)
- [Installation](#installation)
- [Core Concepts](#core-concepts)
- [Building Applications](#building-applications)
- [Models](#models)
- [Knowledge Base (RAG)](#knowledge-base-rag)
- [Tools and Plugins](#tools-and-plugins)
- [Workflows](#workflows)
- [Agents](#agents)
- [Web Apps and Embedding](#web-apps-and-embedding)
- [Using the APIs](#using-the-apis)
- [SDKs](#sdks)
- [Common Tasks](#common-tasks)
- [How It Works](#how-it-works)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Reference](#reference)

---

## Quick Start

Get Dify running locally in under 5 minutes with Docker Compose.

### Prerequisites

- Docker Engine >= 19.03 and Docker Compose >= 2.x
- At least 4 GB RAM (8 GB recommended)
- x86_64 or ARM64 CPU

### Steps

```bash
# Clone the repository
git clone https://github.com/langgenius/dify.git
cd dify/docker

# Copy the example environment file
cp .env.example .env

# Start all services
docker compose up -d
```

Once containers are healthy (check with `docker compose ps`), open `http://localhost` in your browser. You will see the initial setup screen.

### First-Time Setup

1. Navigate to `http://localhost/install`
2. Create your admin account (email + password)
3. Set up your first workspace
4. Add at least one LLM provider (e.g., OpenAI, Anthropic, or a local Ollama instance)

> [!IMPORTANT]
> The `INIT_PASSWORD` environment variable, if set, is required during the `/install` step. This provides an extra layer of protection for the initial setup endpoint.

After setup, the `/install` endpoint is permanently disabled for that deployment.

---

## Installation

### Docker Compose (Recommended)

The `docker/` directory contains a production-ready `docker-compose.yaml` that orchestrates all required services:

| Container | Role | Default Port |
|-----------|------|-------------|
| `nginx` | Reverse proxy, static files | 80, 443 |
| `web` | Next.js frontend | 3000 (internal) |
| `api` | Flask API server | 5001 (internal) |
| `worker` | Celery background worker | -- |
| `db` | PostgreSQL database | 5432 (internal) |
| `redis` | Cache, queue broker, sessions | 6379 (internal) |
| `weaviate` | Default vector store | 8080 (internal) |
| `plugin_daemon` | Go-based plugin runtime | 5002 (internal) |
| `sandbox` | Secure code execution | 8194 (internal) |
| `ssrf_proxy` | Outbound request proxy | 3128 (internal) |
| `certbot` | SSL certificate automation | -- |

```bash
cd dify/docker
cp .env.example .env
# Edit .env to configure your deployment (see Configuration below)
docker compose up -d
```

#### Environment Sync Tool

When upgrading Dify, new environment variables may be added to `.env.example`. Use the built-in sync tool to merge them:

```bash
python docker/dify-env-sync.py
# Options:
#   --dir <path>    Working directory (default: current)
#   --no-backup     Skip timestamped .env backup
```

This preserves your existing values while adding new variables with their defaults.

### Source Installation

For development or customization:

#### Backend (API)

```bash
cd api
# Install uv (Python package manager)
pip install uv
# Create virtual environment and install dependencies
uv sync --all-extras
# Copy environment configuration
cp .env.example .env
# Initialize database
uv run flask upgrade-db
# Start the API server
uv run gunicorn --bind 0.0.0.0:5001 --worker-class gevent app:app
```

#### Backend (Worker)

```bash
cd api
uv run celery -A app.celery worker -Q dataset,generation,mail,ops_trace --loglevel INFO
```

#### Frontend

```bash
cd web
# Requires Node.js 22.22.1+ and pnpm 10.33.0+
pnpm install
pnpm dev
# Production build:
pnpm build && pnpm start
```

> [!NOTE]
> The frontend uses `pnpm` exclusively -- `npm` and `yarn` are not supported. The `preinstall` script enforces this with `only-allow pnpm`.

### System Requirements

| Component | Minimum | Recommended |
|-----------|---------|-------------|
| CPU | 2 cores | 4+ cores |
| RAM | 4 GB | 8+ GB |
| Disk | 20 GB | 50+ GB (grows with knowledge base) |
| Docker | 19.03+ | Latest stable |
| Docker Compose | 2.x | Latest stable |
| Node.js | 22.22.1 | 22.22.1 |
| Python | 3.12+ | 3.12+ |
| PostgreSQL | 15+ | 16+ |
| Redis | 6+ | 7+ |

### Upgrading

```bash
cd dify/docker
git pull origin main
docker compose down
docker compose pull
# Sync any new environment variables
python dify-env-sync.py
docker compose up -d
```

Database migrations run automatically on API container startup via `flask upgrade-db`.

---

## Core Concepts

### Application Types

Dify supports five distinct application types, each suited to different use cases:

| Type | Description | Use Case |
|------|-------------|----------|
| **Chat** | Multi-turn conversational interface | Customer support, FAQ bots |
| **Completion** | Single prompt to single response | Content generation, summarization |
| **Workflow** | Visual DAG-based automation | Data processing, multi-step reasoning |
| **Agent-Chat** | Chat with autonomous tool use | Research assistants, data analysts |
| **Advanced-Chat** | Chat backed by a workflow | Complex conversational flows with branching logic |

### Workspaces and Tenants

A **workspace** (internally called a "tenant") is the top-level organizational unit. Each workspace has:

- Its own set of applications, datasets, and model configurations
- Member management with role-based access (Owner, Admin, Editor, Viewer, Dataset Operator)
- Separate API key scoping
- Independent usage quotas

### Models and Providers

Dify acts as a model abstraction layer. You configure **model providers** (OpenAI, Anthropic, Azure OpenAI, Google, local Ollama/vLLM, etc.) in Settings, then select specific models per application. This means:

- Swap models without changing application logic
- Use different models for different tasks (cheap model for classification, powerful model for generation)
- Self-hosted models work the same as cloud APIs

### Datasets (Knowledge Base)

A **dataset** is a collection of documents processed for retrieval. Documents go through:

1. **Upload** -- files (PDF, DOCX, TXT, Markdown, HTML, CSV, Excel, EPUB) or web crawl
2. **Processing** -- extraction, cleaning, chunking
3. **Embedding** -- vector embeddings stored in the configured vector database
4. **Indexing** -- full-text and/or semantic search indexes

Datasets can be shared across multiple applications.

### Plugins

Plugins extend Dify's capabilities. They run in a separate Go-based daemon (`plugin_daemon`) and communicate via JSON-RPC. Plugin types include:

- **Model providers** -- add new LLM/embedding/reranker backends
- **Tools** -- give agents and workflows callable actions
- **Extensions** -- custom endpoints, triggers, data sources

Plugins are distributed as `.difypkg` files and can be installed from the Marketplace or uploaded manually.

---

## Building Applications

### Creating an App

1. From the dashboard, click **Create App**
2. Choose your application type (Chat, Completion, Workflow, Agent-Chat, or Advanced-Chat)
3. Name your app and optionally provide a description and icon

### Configuring a Chat App

1. **Model** -- Select the LLM and adjust parameters (temperature, top-p, max tokens, etc.)
2. **System Prompt** -- Define the persona, instructions, and constraints
3. **Context** -- Attach datasets for RAG-powered responses
4. **Features** -- Enable/disable:
   - Citation and attribution
   - Content moderation (OpenAI moderation, custom keywords, or API-based)
   - Speech-to-text / Text-to-speech
   - Suggested follow-up questions
   - Opening statement / Suggested openers

### Configuring a Completion App

1. **Model** -- Same model selection as Chat
2. **Prompt Template** -- Write a prompt with `{{variable}}` placeholders
3. **Variables** -- Define input variables that users fill in (text, paragraph, select, number)
4. **Context** -- Optional RAG attachment

### Variables

Variables let you create dynamic, reusable prompts:

| Type | Description |
|------|-------------|
| `text-input` | Short single-line text |
| `paragraph` | Multi-line text area |
| `select` | Dropdown with predefined options |
| `number` | Numeric input |
| `file` | File upload (images, documents) |
| `file-list` | Multiple file uploads |

Reference variables in prompts with `{{variable_name}}` syntax.

### Orchestration Modes

- **Simple** -- Direct prompt, model, and response (suitable for most chat/completion apps)
- **Workflow** -- Visual DAG editor for complex logic (see [Workflows](#workflows))

### Publishing and Versioning

Once configured, you can:

- **Run/Preview** -- Test in the built-in playground
- **Publish** -- Make the app available via API and web app
- **Version History** -- DSL-based export/import for application configurations

> [!TIP]
> Export your app configuration as YAML DSL for version control, backup, or sharing. Use **Export DSL** from the app settings to download the configuration file.

---

## Models

### Adding Model Providers

Navigate to **Settings** and configure:

1. **Cloud APIs** -- Enter API keys for OpenAI, Anthropic, Google, Azure OpenAI, Cohere, Minimax, Zhipu, Baichuan, and others
2. **Self-hosted Models** -- Point to Ollama, vLLM, Xinference, LocalAI, or any OpenAI-compatible endpoint
3. **Custom Models** -- Add custom model definitions with specific parameters

### Model Types

| Type | Purpose | Example |
|------|---------|---------|
| LLM | Text generation | GPT-4o, Claude Sonnet |
| Text Embedding | Vector embeddings for RAG | text-embedding-3-large |
| Reranker | Re-rank search results | bge-reranker-v2-m3 |
| Speech-to-Text | Audio transcription | Whisper |
| Text-to-Speech | Voice synthesis | TTS-1 |
| Moderation | Content filtering | text-moderation-latest |

### Model Parameters

When selecting a model in an app, you can tune:

- **Temperature** -- Randomness (0 = deterministic, 1 = creative)
- **Top P** -- Nucleus sampling threshold
- **Max Tokens** -- Response length limit
- **Presence Penalty** -- Penalize repeated topics
- **Frequency Penalty** -- Penalize repeated tokens
- **Stop Sequences** -- Tokens that terminate generation

### Default Models

Set system-wide defaults in **Settings** under System Model Settings:

- Default LLM for inference
- Default embedding model for datasets
- Default reranker for search
- Default speech-to-text / text-to-speech models

---

## Knowledge Base (RAG)

### Creating a Dataset

1. Go to **Knowledge** in the sidebar
2. Click **Create Knowledge** and choose a data source:
   - **Upload files** -- PDF, DOCX, TXT, MD, HTML, CSV, XLSX, EPUB
   - **Sync from Notion** -- Connect Notion workspace pages
   - **Sync from website** -- Crawl via Firecrawl, Jina Reader, or WaterCrawl

3. Configure processing settings:
   - **Chunking strategy** -- Automatic, custom (separator + chunk size), or parent-child
   - **Chunk size** -- Token count per segment (default: 500, max configurable via `INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH`, default 4000)
   - **Chunk overlap** -- Overlapping tokens between segments
   - **Cleaning rules** -- Remove extra whitespace, URLs, emails

4. Choose indexing mode:
   - **High Quality** -- Embedding + vector search (requires embedding model)
   - **Economical** -- Keyword-based full-text search (no embedding needed)

### Retrieval Modes

When attaching a dataset to an app, choose the retrieval strategy:

| Mode | How It Works | Best For |
|------|-------------|----------|
| **Semantic Search** | Vector similarity (cosine/L2) | Natural language queries |
| **Full-Text Search** | Keyword/BM25 matching | Exact term lookups |
| **Hybrid Search** | Both semantic + full-text, merged | Best overall recall |

Parameters:
- **Top K** -- Number of chunks to retrieve (configurable max via `NEXT_PUBLIC_TOP_K_MAX_VALUE`)
- **Score Threshold** -- Minimum relevance score (0-1)
- **Reranker** -- Optional re-ranking model for improved precision

### Multi-Dataset Retrieval

Apps can query multiple datasets simultaneously. The **N-Choose-Top** strategy retrieves from all datasets in parallel and merges results by relevance score.

### Maintaining Datasets

- **Add documents** -- Upload additional files or sync new pages
- **Re-process** -- Re-chunk and re-embed existing documents with new settings
- **Disable/Enable segments** -- Toggle individual chunks without deleting
- **Metadata** -- View and manage document metadata
- **Hit Testing** -- Test retrieval with sample queries to tune settings

> [!IMPORTANT]
> Re-processing a large dataset triggers re-embedding of all chunks, which consumes embedding model quota. Check your provider's rate limits before bulk re-processing.

---

## Tools and Plugins

### Built-in Tools

Dify ships with built-in tool integrations including:

- **Web Search** -- Google, Bing, DuckDuckGo, SearXNG, Serper
- **Web Browsing** -- Fetch and parse web pages
- **Math** -- Calculator, Wolfram Alpha
- **Code** -- Sandboxed Python/JavaScript execution
- **Image** -- DALL-E, Stable Diffusion
- **Data** -- Wikipedia, arXiv, PubMed

### Custom Tools

Create tools using **OpenAPI/Swagger** specifications:

1. Go to **Tools** and click **Create Custom Tool**
2. Paste your OpenAPI 3.0 schema (JSON or YAML)
3. Configure authentication (None, API Key, or OAuth 2.0)
4. Test the tool endpoints

### Plugin Marketplace

Browse and install community plugins from the built-in Marketplace:

1. Go to **Plugins** in the sidebar
2. Browse or search the marketplace
3. Click **Install** to add a plugin

> [!NOTE]
> The marketplace is enabled by default (`MARKETPLACE_ENABLED=true`). It connects to `https://marketplace.dify.ai`. Set `MARKETPLACE_ENABLED=false` in your `.env` to disable it for air-gapped deployments.

### Plugin Configuration

- **Model Provider plugins** -- Appear in Settings after installation
- **Tool plugins** -- Appear in the Tools panel within apps
- **Extension plugins** -- May add custom endpoints or webhooks

Plugins run in the `plugin_daemon` container, isolated from the main API. Communication is via JSON-RPC over stdio.

### Tool and Provider Visibility

Control which tools and providers appear in the UI:

| Variable | Purpose |
|----------|---------|
| `POSITION_TOOL_PINS` | Pin specific tools to top of list |
| `POSITION_TOOL_INCLUDES` | Whitelist -- only show these tools |
| `POSITION_TOOL_EXCLUDES` | Blacklist -- hide these tools |
| `POSITION_PROVIDER_PINS` | Pin specific providers to top |
| `POSITION_PROVIDER_INCLUDES` | Whitelist providers |
| `POSITION_PROVIDER_EXCLUDES` | Blacklist providers |

### Endpoint-Based Extensions

Create custom HTTP endpoints served by plugins:

```
ENDPOINT_URL_TEMPLATE=http://localhost:5002/e/{hook_id}
```

Each endpoint gets a unique `hook_id` and can be configured with custom authentication.

---

## Workflows

### Overview

The Workflow editor is a visual, drag-and-drop DAG (Directed Acyclic Graph) builder. Each node performs an action, and edges define execution order. Workflows support parallel branches, conditional routing, loops, and human-in-the-loop approvals.

### Node Types

| Node | Description |
|------|-------------|
| **Start** | Entry point -- defines input variables |
| **End** | Terminal node -- defines output variables |
| **LLM** | Call a language model with a prompt template |
| **Knowledge Retrieval** | Query datasets for RAG |
| **Question Classifier** | Route based on intent classification |
| **IF/ELSE** | Conditional branching |
| **Code** | Execute Python or JavaScript in sandbox |
| **Template** | Jinja2 template rendering |
| **Variable Aggregator** | Merge variables from parallel branches |
| **Variable Assigner** | Set or transform variables |
| **Iteration** | Loop over a list, processing each item |
| **Parameter Extractor** | Extract structured data from LLM output |
| **HTTP Request** | Call external APIs |
| **Tool** | Invoke a configured tool |
| **Sub-Workflow** | Call another workflow |
| **Assigner** | Direct variable assignment |
| **Document Extractor** | Extract text from uploaded files |
| **List Filter** | Filter arrays by condition |

### Execution Limits

| Parameter | Default | Env Variable |
|-----------|---------|-------------|
| Max steps per run | 500 | `WORKFLOW_MAX_EXECUTION_STEPS` |
| Max execution time | 1200s (20 min) | `WORKFLOW_MAX_EXECUTION_TIME` |
| Max call depth (sub-workflows) | 5 | `WORKFLOW_CALL_MAX_DEPTH` |
| Max variable size | 200 KB | `MAX_VARIABLE_SIZE` |
| Max parallel submit count | 100 | `MAX_SUBMIT_COUNT` |
| Max loop iterations | Configurable | `NEXT_PUBLIC_LOOP_NODE_MAX_COUNT` |

### Triggers

Workflows can be started by:

- **Manual** -- Run from the UI or via API
- **Webhook** -- Incoming HTTP requests trigger execution (`WEBHOOK_REQUEST_BODY_MAX_SIZE` default 10 MB)
- **Schedule (Cron)** -- Time-based triggers
- **Plugin Trigger** -- Events from installed plugins

### Debugging Workflows

- **Single Node Run** -- Test individual nodes in isolation (preview/draft mode)
- **Full Run** -- Execute the entire workflow with test inputs
- **Run History** -- View past executions with input/output for each node
- **Tracing** -- Track execution path through parallel branches

> [!TIP]
> Use the **Single Node Run** feature to debug complex LLM prompts or code nodes before wiring them into the full workflow.

---

## Agents

### Agent-Chat Apps

Agent-Chat apps give the LLM autonomous tool-calling ability. The agent decides when and how to use tools based on the conversation context.

### Configuration

1. Create an **Agent-Chat** app
2. Select a model that supports function calling
3. Add tools from:
   - Built-in tools
   - Custom tools (OpenAPI)
   - Plugin tools
4. Configure agent settings:
   - **Max iterations** -- Limit tool-calling loops (configurable via `NEXT_PUBLIC_MAX_ITERATIONS_NUM`)
   - **Max tools per turn** -- Limit concurrent tool usage (configurable via `NEXT_PUBLIC_MAX_TOOLS_NUM`)

### Advanced-Chat Apps

Advanced-Chat combines the conversational interface of Chat with the power of Workflows:

1. Create an **Advanced-Chat** app
2. Build a workflow that processes the user's message
3. The workflow can include RAG retrieval, tool calls, conditional logic, and multi-step reasoning
4. The final output node feeds back into the chat interface

This is the most flexible app type -- use it when simple prompt engineering is not sufficient but you want a conversational UX.

---

## Web Apps and Embedding

### Built-in Web App

Every published app automatically gets a shareable web app:

1. Go to your app and open the Monitoring section or click the globe icon
2. Share the URL with users
3. Optionally customize:
   - App title and description
   - Welcome message / conversation openers
   - Color theme
   - Custom domain (via `NEXT_PUBLIC_WEB_PREFIX`)

### Embedding in Your Website

Embed Dify apps into existing websites using:

#### iframe

```html
<iframe
  src="https://your-dify-instance.com/chatbot/APP_TOKEN"
  width="100%"
  height="600"
  frameborder="0">
</iframe>
```

#### Script Tag

```html
<script
  src="https://your-dify-instance.com/embed.min.js"
  id="APP_TOKEN"
  defer>
</script>
```

> [!NOTE]
> Embedding requires `NEXT_PUBLIC_ALLOW_EMBED=true` in your frontend environment. For inline styles, also set `NEXT_PUBLIC_ALLOW_INLINE_STYLES=true`.

---

## Using the APIs

Dify exposes two primary API surfaces for programmatic access.

### Service API (for your back-end)

Base URL: `https://your-dify-instance.com/v1`

Authenticate with your app's **API Key** via the `Authorization: Bearer {api_key}` header.

#### Chat / Agent-Chat Endpoints

```bash
# Send a message (streaming)
curl -X POST https://your-dify-instance.com/v1/chat-messages \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {},
    "query": "What is Dify?",
    "response_mode": "streaming",
    "user": "user-123"
  }'

# Get conversation history
curl https://your-dify-instance.com/v1/messages?conversation_id={conv_id}&user=user-123 \
  -H "Authorization: Bearer {api_key}"

# List conversations
curl https://your-dify-instance.com/v1/conversations?user=user-123 \
  -H "Authorization: Bearer {api_key}"
```

#### Completion Endpoints

```bash
curl -X POST https://your-dify-instance.com/v1/completion-messages \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {"topic": "quantum computing"},
    "response_mode": "blocking",
    "user": "user-123"
  }'
```

#### Workflow Endpoints

```bash
# Run a workflow
curl -X POST https://your-dify-instance.com/v1/workflows/run \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {"data": "process this"},
    "response_mode": "streaming",
    "user": "user-123"
  }'

# Get workflow run status
curl https://your-dify-instance.com/v1/workflows/run/{workflow_run_id} \
  -H "Authorization: Bearer {api_key}"
```

#### File Upload

```bash
curl -X POST https://your-dify-instance.com/v1/files/upload \
  -H "Authorization: Bearer {api_key}" \
  -F "file=@document.pdf" \
  -F "user=user-123"
```

Upload limits (configurable):

| Parameter | Default | Env Variable |
|-----------|---------|-------------|
| File size | 15 MB | `UPLOAD_FILE_SIZE_LIMIT` |
| Batch count | 5 | `UPLOAD_FILE_BATCH_LIMIT` |
| Image size | 10 MB | `UPLOAD_IMAGE_FILE_SIZE_LIMIT` |
| Video size | 100 MB | `UPLOAD_VIDEO_FILE_SIZE_LIMIT` |
| Audio size | 50 MB | `UPLOAD_AUDIO_FILE_SIZE_LIMIT` |

#### Annotations

Annotations let you define exact input-to-output pairs that override model responses:

```bash
# Create an annotation
curl -X POST https://your-dify-instance.com/v1/apps/annotations \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "What is your return policy?",
    "answer": "We offer 30-day returns on all items."
  }'
```

#### Response Modes

| Mode | Behavior |
|------|----------|
| `blocking` | Wait for complete response, return as JSON |
| `streaming` | Server-Sent Events (SSE), tokens delivered as generated |

SSE streaming events include: `message`, `agent_message`, `agent_thought`, `message_file`, `message_end`, `tts_message`, `tts_message_end`, `error`, `ping`, `workflow_started`, `node_started`, `node_finished`, `workflow_finished`.

### Console API (for the web UI)

Base URL: `https://your-dify-instance.com/console/api`

Used by the Dify web interface. Authenticated via session tokens (JWT). Not intended for external integration -- use the Service API instead.

> [!WARNING]
> The Console API is an internal interface. Its endpoints may change between versions without notice. Use the Service API (`/v1/*`) for all integrations.

---

## SDKs

### Node.js SDK

```bash
npm install dify-client
```

```javascript
import { DifyClient, ChatClient, CompletionClient } from 'dify-client';

// Initialize
const chatClient = new ChatClient(
  'your-api-key',
  'https://your-dify-instance.com/v1'
);

// Send a chat message (streaming)
const response = await chatClient.createChatMessage(
  {},          // inputs
  'Hello!',    // query
  'user-123',  // user
  true,        // streaming
  null         // conversation_id (null for new)
);

// Send a completion message
const completionClient = new CompletionClient('your-api-key');
const result = await completionClient.createCompletionMessage(
  { topic: 'AI' },  // inputs
  'user-123',        // user
  false              // streaming
);

// Run a workflow
const difyClient = new DifyClient('your-api-key');
const workflowResult = await difyClient.runWorkflow(
  { data: 'process this' },  // inputs
  'user-123',                 // user
  true                        // streaming
);
```

Requires Node.js >= 18.0.0. Published as `dify-client` v3.1.0 on npm.

### PHP SDK

```php
require 'vendor/autoload.php';

// Chat
$client = new DifyClient('your-api-key');
$chatClient = new ChatClient($client);
$response = $chatClient->chat(
    ['query' => 'Hello!', 'user' => 'user-123']
);

// Completion
$completionClient = new CompletionClient($client);
$response = $completionClient->create(
    ['inputs' => ['topic' => 'AI'], 'user' => 'user-123']
);
```

Requires PHP >= 7.2, Guzzle >= 7.9. Install via Composer.

### Python SDK

A separate Python SDK repository is maintained at `github.com/langgenius/dify-sdk-python`. Refer to that repository for installation and usage.

---

## Common Tasks

### Import/Export App Configurations

**Export:**
```
App Settings -> Export DSL -> Save the YAML file
```

**Import:**
```
Dashboard -> Import DSL -> Select YAML file
```

DSL files capture the complete app configuration including prompts, model settings, tool bindings, and workflow definitions.

### Duplicate an App

From the dashboard, click the menu icon on any app and select **Duplicate**. This creates an independent copy with all settings.

### Add Conversation Openers

In your Chat or Agent-Chat app:
1. Go to **Features** and find **Opening Statement**
2. Write a welcome message
3. Optionally add suggested opener buttons (pre-filled questions users can click)

### Enable Citation

In Chat/Agent-Chat apps with RAG:
1. Go to **Features** and find **Citation and Attribution**
2. Toggle on -- responses will include source references from retrieved chunks

### Content Moderation

Three moderation approaches:

1. **OpenAI Moderation** -- Uses OpenAI's moderation API to filter harmful content
2. **Custom Keywords** -- Define blocked words/phrases; matches trigger a preset response
3. **External API** -- Call your own moderation endpoint for custom logic

### Data Source Integrations

Connect external data via:

- **Notion** -- Sync pages as knowledge base documents
  - Configure `NOTION_INTEGRATION_TYPE` (`public` or `internal`)
  - Set `NOTION_CLIENT_ID`, `NOTION_CLIENT_SECRET`, or `NOTION_INTERNAL_SECRET`
- **Website Crawl** -- Pull content from URLs using:
  - Firecrawl (`NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL`)
  - Jina Reader (`NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER`)
  - WaterCrawl (`NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL`)

### Batch Processing

For high-throughput scenarios:
- Set `NEXT_PUBLIC_BATCH_CONCURRENCY` to control parallel request handling
- Use `BATCH_UPLOAD_LIMIT` (default: 10) to control simultaneous file uploads
- Weaviate batching: `WEAVIATE_BATCH_SIZE` (default: 100)

### Logs and Monitoring

- **App Logs** -- View request/response history per app in the **Logs** tab
- **Workflow Runs** -- Detailed execution traces with per-node timing and I/O
- **Annotations** -- Review and manage human-curated responses
- **API Request Monitoring** -- Track usage, latency, and errors

### Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Enter` | Send message (chat) |
| `Shift + Enter` | New line in message |
| `Ctrl/Cmd + Z` | Undo (workflow editor) |
| `Ctrl/Cmd + Y` | Redo (workflow editor) |
| `Delete/Backspace` | Delete selected node |

---

## How It Works

This section provides a high-level understanding of Dify's architecture for users who want to understand what happens behind the scenes. For deep architectural details, see the [Admin Guide](langgenius__dify__AdminGuide.md).

### Request Flow

When you send a message to a Chat app:

```
User -> nginx -> Next.js (web) -> Flask API (api) -> LLM Provider
                                      |
                                 Redis (cache)
                                      |
                                PostgreSQL (state)
                                      |
                            Vector Store (RAG retrieval)
```

1. The frontend sends the request to the Flask API
2. The API authenticates and loads the app configuration
3. If RAG is enabled, it queries the vector store for relevant chunks
4. The prompt is assembled with context and sent to the LLM
5. The response streams back through SSE to the frontend

### RAG Pipeline

```
Document -> Extractor -> Cleaner -> Chunker -> Embedder -> Vector Store
```

1. **Extractor** -- Parses file format (PDF, DOCX, etc.) into raw text
2. **Cleaner** -- Removes noise (extra whitespace, boilerplate, etc.)
3. **Chunker** -- Splits text into segments based on your settings
4. **Embedder** -- Generates vector embeddings via your configured model
5. **Vector Store** -- Stores embeddings for similarity search

At query time:
```
Query -> Embedder -> Vector Search -> (optional) Reranker -> Context Assembly -> LLM
```

### Workflow Execution

The workflow engine executes DAGs using a graph engine with parallel branch support:

1. Start at the **Start** node
2. Execute nodes in topological order
3. At parallel branches, nodes run concurrently via a thread pool (`MAX_SUBMIT_COUNT`)
4. **Variable Aggregator** nodes wait for all upstream branches before proceeding
5. The **End** node collects final outputs

### Plugin Architecture

```
Flask API <-> plugin_daemon (Go) <-> Plugin Process (stdio JSON-RPC)
```

Plugins run as isolated processes managed by the Go-based `plugin_daemon`. Communication uses JSON-RPC over stdio, providing process isolation and language independence.

---

## Troubleshooting

### Installation Issues

#### Docker containers fail to start

**Symptom:** `docker compose up -d` exits with errors or containers restart in a loop.

**Solutions:**
1. Check available RAM -- Dify requires at least 4 GB
2. Verify Docker Engine and Compose versions meet requirements
3. Check port conflicts -- ports 80, 443, 5432, 6379 must be free
4. Review logs: `docker compose logs api` or `docker compose logs worker`

#### Database migration fails on startup

**Symptom:** API container crashes with migration errors.

**Solutions:**
1. Ensure PostgreSQL is fully started before the API: check `docker compose logs db`
2. Run migration manually: `docker compose exec api flask upgrade-db`
3. Check `DB_*` environment variables match your PostgreSQL configuration

#### Frontend shows blank page

**Symptom:** `http://localhost` loads but shows a white screen.

**Solutions:**
1. Check `CONSOLE_API_URL` and `CONSOLE_WEB_URL` in `.env` match your setup
2. Verify the `web` container is running: `docker compose ps web`
3. Check browser console for CORS errors -- update `CONSOLE_CORS_ALLOW_ORIGINS`

#### Weaviate gRPC connection failure

**Symptom:** Vector operations fail after upgrading Weaviate.

**Solution:** Weaviate v1.27+ requires gRPC on port `50051` in addition to HTTP on port `8080`. Ensure both ports are exposed in your Docker Compose configuration.

### Model Issues

#### "Model not found" or "Invalid API key"

**Solutions:**
1. Verify API key in Settings
2. Check that the model name exactly matches the provider's documentation
3. For Azure OpenAI, ensure the deployment name matches your configured model
4. For self-hosted models (Ollama/vLLM), verify the endpoint URL is reachable from the API container

#### Slow model responses

**Solutions:**
1. Check `NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS` -- increase if requests time out
2. For self-hosted models, verify GPU utilization and model loading status
3. Reduce `Max Tokens` if responses are unnecessarily long
4. For Ollama, ensure the model is loaded in memory (first request loads from disk)

#### Streaming not working

**Solutions:**
1. Ensure your reverse proxy supports SSE (no buffering)
2. nginx: add `proxy_buffering off;` to your location block
3. Cloudflare: disable response buffering or use WebSocket mode
4. Check `NEXT_PUBLIC_SOCKET_URL` if using WebSocket transport

### Knowledge Base Issues

#### Documents stuck in "Processing"

**Symptom:** Uploaded documents never finish indexing.

**Solutions:**
1. Check Celery worker is running: `docker compose ps worker`
2. Check Redis connectivity: `docker compose exec redis redis-cli ping`
3. Review worker logs: `docker compose logs worker`
4. Verify embedding model is configured and has available quota

#### Poor retrieval quality

**Solutions:**
1. **Test retrieval** -- Use Hit Testing to check what chunks are returned for sample queries
2. **Adjust chunk size** -- Try smaller chunks (200-300 tokens) for precise answers, larger (800-1000) for context-rich answers
3. **Enable hybrid search** -- Combine semantic + full-text for better recall
4. **Add a reranker** -- Reranking models significantly improve precision
5. **Review chunk overlap** -- 50-100 token overlap helps preserve context across boundaries

#### Embedding failures

**Solutions:**
1. Check embedding model quota and rate limits
2. Verify `INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH` is not too large for your model
3. For large documents, processing happens in batches -- check worker logs for errors

### Workflow Issues

#### Workflow exceeds maximum steps

**Symptom:** Error "Workflow execution exceeded maximum steps."

**Solution:** Increase `WORKFLOW_MAX_EXECUTION_STEPS` (default: 500) in `.env`, or optimize your workflow to reduce node count. Check for infinite loops in iteration nodes.

#### Workflow times out

**Symptom:** Error after 20 minutes of execution.

**Solution:** Increase `WORKFLOW_MAX_EXECUTION_TIME` (default: 1200 seconds) in `.env`, or break complex workflows into sub-workflows to parallelize processing.

#### Sub-workflow depth limit

**Symptom:** Error "Maximum call depth exceeded."

**Solution:** `WORKFLOW_CALL_MAX_DEPTH` defaults to 5. Flatten your sub-workflow nesting or increase the limit.

#### HTTP Request node fails

**Solutions:**
1. Check `SSRF_PROXY_HTTP_URL` / `SSRF_PROXY_HTTPS_URL` -- all outbound requests from workflow nodes go through the SSRF proxy
2. Verify timeout settings: `HTTP_REQUEST_MAX_CONNECT_TIMEOUT` (300s), `HTTP_REQUEST_MAX_READ_TIMEOUT` (600s)
3. Check response size limits: `HTTP_REQUEST_NODE_MAX_BINARY_SIZE` (10 MB), `HTTP_REQUEST_NODE_MAX_TEXT_SIZE` (1 MB)

### Plugin Issues

#### Plugin fails to install

**Solutions:**
1. Check `plugin_daemon` container is running: `docker compose ps plugin_daemon`
2. Verify `PLUGIN_DAEMON_URL` (default: `http://127.0.0.1:5002`)
3. Check package size limit: `PLUGIN_MAX_PACKAGE_SIZE` (default: 15 MB / 15728640 bytes)
4. Review daemon logs: `docker compose logs plugin_daemon`

#### Plugin cannot connect to Marketplace

**Solutions:**
1. Verify `MARKETPLACE_ENABLED=true` in `.env`
2. Check `MARKETPLACE_API_URL` (default: `https://marketplace.dify.ai`)
3. Ensure outbound HTTPS is not blocked by firewall

### API Issues

#### CORS errors

**Solutions:**
1. Set `WEB_API_CORS_ALLOW_ORIGINS` to include your frontend domain
2. Set `CONSOLE_CORS_ALLOW_ORIGINS` to include your console domain
3. Use `*` for development (not recommended for production)

#### Rate limiting / "Too many requests"

**Solutions:**
1. Adjust `APP_MAX_ACTIVE_REQUESTS` (0 = unlimited)
2. Check `APP_DEFAULT_ACTIVE_REQUESTS` for the per-app default
3. Review your LLM provider's rate limits

#### File upload fails

**Solutions:**
1. Check file size against `UPLOAD_FILE_SIZE_LIMIT` (default: 15 MB)
2. Verify storage configuration -- `STORAGE_TYPE` and associated credentials
3. For S3-compatible storage, check bucket permissions and CORS

### Performance Issues

#### Slow startup

**Solution:** First startup is slowest -- database migrations, model downloads, and index creation all happen. Subsequent starts are faster. The `web` container runs `next build` which can take several minutes.

#### High memory usage

**Solutions:**
1. Redis memory: set `maxmemory` with an eviction policy
2. Worker memory: limit Celery worker concurrency with `-c` flag
3. Vector store: large datasets consume significant memory -- consider using a dedicated vector DB instance

#### Sandbox code execution timeout

**Solutions:**
1. Check `CODE_EXECUTION_ENDPOINT` (default: `http://127.0.0.1:8194`)
2. Verify `CODE_EXECUTION_API_KEY` matches between API and sandbox containers
3. Adjust code size limits if hitting boundaries

### Authentication Issues

#### "Login Locked" after failed attempts

**Solution:** Login lockout duration is controlled by `LOGIN_LOCKOUT_DURATION` (default: 86400 seconds / 24 hours). Wait for lockout to expire or reset via CLI:

```bash
docker compose exec api flask reset-password \
  --email [email removed] \
  --new-password "NewPassword123" \
  --password-confirm "NewPassword123"
```

#### Password reset token expired

**Solution:** Token validity is configured via `RESET_PASSWORD_TOKEN_EXPIRY_MINUTES` (default: 5 minutes). Request a new reset link.

#### Email registration disabled

**Solution:** Check mail configuration -- `MAIL_TYPE` must be set to `resend` or `smtp` with valid credentials for email-based features.

### Proxy and Network Issues

#### Outbound requests blocked

**Solutions:**
1. Configure proxy: `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`
2. Check `SSRF_PROXY_*` settings for workflow HTTP nodes
3. SSRF proxy defaults: `SSRF_DEFAULT_TIME_OUT=5`, `SSRF_DEFAULT_MAX_RETRIES=3`

#### WebSocket disconnections

**Solutions:**
1. Check `NEXT_PUBLIC_SOCKET_URL` points to your API
2. Ensure reverse proxy supports WebSocket upgrades
3. Verify `RESPECT_XFORWARD_HEADERS_ENABLED` if behind a load balancer

> [!TIP]
> Most configuration problems can be diagnosed by examining container logs: `docker compose logs <service_name>`. Add `--tail 100` to see the most recent entries.

---

## FAQ

**Q: What LLM providers does Dify support?**
A: Dify supports 50+ providers including OpenAI, Anthropic, Google (Gemini/PaLM), Azure OpenAI, AWS Bedrock, Cohere, Minimax, Zhipu, Baichuan, Ollama, vLLM, Xinference, LocalAI, and any OpenAI-compatible endpoint. New providers can be added via plugins.

**Q: Can I run Dify completely offline / air-gapped?**
A: Yes. Use self-hosted models (Ollama, vLLM) for LLM inference, disable the marketplace (`MARKETPLACE_ENABLED=false`), and pre-install any required plugins. All core features work without internet access.

**Q: What vector databases are supported?**
A: 25+ backends: Weaviate (default), Qdrant, pgvector, pgvecto-rs, Milvus, Chroma, Elasticsearch, OpenSearch, TiDB Vector, Oracle, OceanBase, Couchbase, Tencent, Baidu, Hologres, AnalyticDB, Alibaba Cloud MySQL, Lindorm, Upstash, VikingDB, MyScale, Tablestore, Huawei Cloud, IRIS, MatrixOne, Clickzetta, Relyt, OpenGauss, Vastbase, SeekDB. Set `VECTOR_STORE` in `.env`.

**Q: How do I switch vector databases?**
A: Set `VECTOR_STORE=<backend>` in `.env` and restart. For existing datasets, use the CLI migration command:
```bash
docker compose exec api flask vdb-migrate --scope all
```

**Q: What file formats can I upload to Knowledge Base?**
A: PDF, DOCX, TXT, Markdown (.md), HTML, CSV, XLSX (Excel), and EPUB. Maximum file size defaults to 15 MB (configurable via `UPLOAD_FILE_SIZE_LIMIT`).

**Q: How do I back up my Dify data?**
A: Back up the PostgreSQL database, Redis data, your `.env` file, and the storage directory (uploads, files). For Docker:
```bash
docker compose exec db pg_dump -U postgres dify > backup.sql
```

**Q: Can I use multiple models in a single app?**
A: Yes. In Workflow and Advanced-Chat apps, each LLM node can use a different model. This lets you use a fast/cheap model for classification and a powerful model for generation within the same flow.

**Q: What languages does the UI support?**
A: The console supports 20+ languages including English, Chinese (Simplified/Traditional), Japanese, Korean, German, French, Italian, Spanish, Portuguese (Brazil), Russian, Turkish, Vietnamese, Thai, Arabic, Hindi, Polish, Romanian, Slovenian, Ukrainian, and Farsi.

**Q: How do I set up SSO / OAuth?**
A: Dify supports Google OAuth out of the box. Configure `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in `.env`. Enterprise edition supports additional SSO providers (SAML, OIDC).

**Q: Can I customize the web app appearance?**
A: Yes -- you can set the app title, description, opening statement, suggested questions, and icon. For deeper customization, modify the frontend source in `web/` and rebuild. Key frontend environment variables start with `NEXT_PUBLIC_*`.

**Q: What is the difference between Chat and Advanced-Chat?**
A: Chat uses simple prompt orchestration (prompt + model + optional RAG). Advanced-Chat backs the chat interface with a full Workflow, giving you conditional logic, multi-step processing, tool calls, and parallel execution -- all while maintaining a conversational UX.

**Q: How do I monitor API usage?**
A: Each app has a **Monitoring** tab showing request counts, token usage, latency percentiles, and user activity. For programmatic access, use the Service API's conversation and message endpoints to query usage data.

**Q: What is the `sandbox` container for?**
A: The sandbox provides isolated code execution for Code nodes in workflows and agents. It runs user-submitted Python/JavaScript in a restricted environment with resource limits. Configuration: `CODE_EXECUTION_ENDPOINT`, `CODE_EXECUTION_API_KEY`.

**Q: What is the `ssrf_proxy` and its purpose?**
A: The SSRF proxy is a Squid-based proxy that filters outbound HTTP requests from workflow nodes and tools. It prevents Server-Side Request Forgery attacks by blocking access to internal network addresses. All HTTP Request nodes and tool calls route through this proxy.

**Q: How do I export chat/conversation data?**
A: Use the CLI command to export messages to JSONL format:
```bash
docker compose exec api flask export-app-messages \
  --app-id {APP_ID} --end-before "2026-01-01"
```
Supports `--use-cloud-storage` for direct S3 upload, `--dry-run` for previewing, and `--batch-size` for memory control.

**Q: What happens to old workflow runs?**
A: Workflow run data grows over time. Use the retention CLI commands to manage it:
- `clean-workflow-runs` -- Delete old runs (default: older than 30 days)
- `archive-workflow-runs` -- Archive to S3-compatible storage (default: older than 90 days)
- `restore-workflow-runs` -- Restore from archives
All support `--dry-run` for safe previewing.

**Q: Can I run Dify on ARM64 (Apple Silicon / AWS Graviton)?**
A: Yes. All official Docker images support both `amd64` and `arm64` architectures.

**Q: How do I enable HTTPS / SSL?**
A: The Docker Compose setup includes a Certbot container for automatic Let's Encrypt certificates. Configure your domain in the nginx configuration and ensure ports 80 and 443 are accessible. Alternatively, terminate SSL at your own reverse proxy.

**Q: What is the ETL_TYPE configuration?**
A: `ETL_TYPE` controls document parsing: `dify` (built-in parser, default) or `Unstructured` (uses the Unstructured.io API for advanced parsing). Set `UNSTRUCTURED_API_URL` and `UNSTRUCTURED_API_KEY` if using the latter.

**Q: How do I configure email for password resets and invitations?**
A: Set `MAIL_TYPE` to `resend` (with `RESEND_API_KEY`) or `smtp` (with `SMTP_SERVER`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD`, `SMTP_USE_TLS`). Set `MAIL_DEFAULT_SEND_FROM` for the sender address.

**Q: Is there a way to pin or hide specific model providers and tools?**
A: Yes. Use position configuration in `.env`:
- `POSITION_PROVIDER_PINS` -- Pin providers to the top
- `POSITION_PROVIDER_INCLUDES` -- Whitelist (only show these)
- `POSITION_PROVIDER_EXCLUDES` -- Blacklist (hide these)
- Same pattern for tools: `POSITION_TOOL_PINS`, `POSITION_TOOL_INCLUDES`, `POSITION_TOOL_EXCLUDES`

**Q: How much disk space do I need for Knowledge Base?**
A: Depends on your vector store and data volume. As a rough guide: 1 million 1536-dimensional vectors is approximately 6 GB of vector storage. Text chunks, metadata, and indexes add overhead. Monitor `STORAGE_TYPE` storage for uploaded files separately.

**Q: How do I use Dify with a proxy?**
A: Set `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables. For LLM API calls, these are respected automatically. For workflow HTTP Request nodes, configure `SSRF_PROXY_HTTP_URL` and `SSRF_PROXY_HTTPS_URL` separately.

**Q: What is multimodal input and how do I use it?**
A: Dify supports sending images alongside text to multimodal models (e.g., GPT-4o, Claude). Upload images via the file upload feature in chat, or via the `/v1/files/upload` API. The `MULTIMODAL_SEND_FORMAT` setting controls whether images are sent as `base64` (default) or `url`.

**Q: Can I schedule workflows to run automatically?**
A: Yes. Use the **Schedule (Cron)** trigger on Workflow apps. Define cron expressions for recurring execution. The `CELERY_BEAT_SCHEDULER_TIME` setting (default: 1 second) controls the scheduler's polling interval.

**Q: What is the `scarf_no_analytics` setting?**
A: `SCARF_NO_ANALYTICS=true` disables Scarf analytics tracking (anonymous install telemetry). Set this in `.env` if you prefer not to send any telemetry data.

---

## Reference

### Environment Variables -- Quick Reference

#### Core URLs

| Variable | Default | Description |
|----------|---------|-------------|
| `CONSOLE_API_URL` | `http://127.0.0.1:5001` | Backend API URL for the console |
| `CONSOLE_WEB_URL` | `http://127.0.0.1:3000` | Frontend URL for the console |
| `SERVICE_API_URL` | `http://127.0.0.1:5001` | Service API base URL |
| `APP_WEB_URL` | `http://127.0.0.1:3000` | Web app base URL |
| `FILES_URL` | `http://127.0.0.1:5001` | File serving base URL |

#### Security

| Variable | Default | Description |
|----------|---------|-------------|
| `SECRET_KEY` | -- | Flask secret key (required) |
| `INIT_PASSWORD` | -- | Initial setup password |
| `FILES_ACCESS_TIMEOUT` | `300` | File URL signature timeout (seconds) |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `60` | JWT access token expiry |
| `REFRESH_TOKEN_EXPIRE_DAYS` | `30` | JWT refresh token expiry |
| `LOGIN_LOCKOUT_DURATION` | `86400` | Account lockout duration (seconds) |

#### Database

| Variable | Default | Description |
|----------|---------|-------------|
| `DB_USERNAME` | `postgres` | PostgreSQL user |
| `DB_PASSWORD` | `difyai123456` | PostgreSQL password |
| `DB_HOST` | `localhost` | PostgreSQL host |
| `DB_PORT` | `5432` | PostgreSQL port |
| `DB_DATABASE` | `dify` | Database name |

#### Redis

| Variable | Default | Description |
|----------|---------|-------------|
| `REDIS_HOST` | `localhost` | Redis host |
| `REDIS_PORT` | `6379` | Redis port |
| `REDIS_PASSWORD` | `difyai123456` | Redis password |
| `REDIS_DB` | `0` | Redis database number |
| `CELERY_BROKER_URL` | `redis://...:6379/1` | Celery broker URL |

#### Storage

| Variable | Default | Description |
|----------|---------|-------------|
| `STORAGE_TYPE` | `opendal` | Storage backend (opendal, s3, azure-blob, etc.) |
| `OPENDAL_SCHEME` | `fs` | OpenDAL scheme (fs, s3, gcs, etc.) |
| `OPENDAL_FS_ROOT` | `storage` | Local filesystem root for OpenDAL |

#### Vector Store

| Variable | Default | Description |
|----------|---------|-------------|
| `VECTOR_STORE` | `weaviate` | Vector database backend |
| `WEAVIATE_ENDPOINT` | `http://localhost:8080` | Weaviate endpoint |
| `WEAVIATE_API_KEY` | -- | Weaviate API key |
| `WEAVIATE_BATCH_SIZE` | `100` | Weaviate batch size |

#### Execution Limits

| Variable | Default | Description |
|----------|---------|-------------|
| `WORKFLOW_MAX_EXECUTION_STEPS` | `500` | Max workflow steps |
| `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | Max workflow time (seconds) |
| `WORKFLOW_CALL_MAX_DEPTH` | `5` | Max sub-workflow nesting |
| `MAX_VARIABLE_SIZE` | `204800` | Max variable size (bytes) |
| `APP_MAX_EXECUTION_TIME` | `1200` | Max app execution time |

#### Logging

| Variable | Default | Description |
|----------|---------|-------------|
| `LOG_LEVEL` | `INFO` | Logging level |
| `LOG_FILE` | -- | Log file path (empty = stdout) |
| `LOG_FILE_MAX_SIZE` | `20` | Max log file size (MB) |
| `LOG_FILE_BACKUP_COUNT` | `5` | Log rotation count |
| `LOG_TZ` | `UTC` | Log timezone |

### Supported Vector Stores -- Complete List

| Backend | Entry Point | Key Dependency |
|---------|------------|----------------|
| Weaviate | `weaviate` | `weaviate-client==4.20.5` |
| Qdrant | `qdrant` | `qdrant-client==1.9.0` |
| pgvector | `pgvector` | `pgvector==0.4.2` |
| pgvecto-rs | `pgvecto-rs` | `pgvecto-rs[sqlalchemy]~=0.2.2` |
| Milvus | `milvus` | `pymilvus~=2.6.12` |
| Chroma | `chroma` | `chromadb==0.5.20` |
| Elasticsearch | `elasticsearch` | `elasticsearch==8.14.0` |
| Elasticsearch (Japanese) | `elasticsearch-ja` | `elasticsearch==8.14.0` |
| OpenSearch | `opensearch` | `opensearch-py==3.1.0` |
| TiDB Vector | `tidb_vector` | `tidb-vector==0.0.15` |
| TiDB on Qdrant | `tidb_on_qdrant` | `qdrant-client==1.9.0` |
| Oracle | `oracle` | `oracledb==3.4.2` |
| OceanBase | `oceanbase` | `pyobvector~=0.2.17` |
| SeekDB | `seekdb` | `pyobvector~=0.2.17` |
| Couchbase | `couchbase` | `couchbase~=4.6.0` |
| Tencent | `tencent` | `tcvectordb~=2.1.0` |
| Baidu | `baidu` | `pymochow==2.4.0` |
| Hologres | `hologres` | `holo-search-sdk>=0.4.2` |
| AnalyticDB | `analyticdb` | `alibabacloud_gpdb20160503~=5.2.0` |
| Alibaba Cloud MySQL | `alibabacloud_mysql` | `mysql-connector-python>=9.3.0` |
| Lindorm | `lindorm` | `opensearch-py==3.1.0` |
| Upstash | `upstash` | `upstash-vector==0.8.0` |
| VikingDB | `vikingdb` | `volcengine-compat~=1.0.0` |
| MyScale | `myscale` | `clickhouse-connect~=0.15.0` |
| Tablestore | `tablestore` | `tablestore==6.4.4` |
| Huawei Cloud | `huawei_cloud` | `elasticsearch==8.14.0` |
| IRIS | `iris` | `intersystems-irispython>=5.1.0` |
| MatrixOne | `matrixone` | `mo-vector~=0.1.13` |
| Clickzetta | `clickzetta` | `clickzetta-connector-python>=0.8.102` |
| Relyt | `relyt` | -- |
| OpenGauss | `opengauss` | -- |
| Vastbase | `vastbase` | `pyobvector~=0.2.17` |

### CLI Commands -- Quick Reference

All CLI commands run via Flask. In Docker: `docker compose exec api flask <command>`.

#### Account Management

| Command | Description |
|---------|-------------|
| `reset-password --email <e> --new-password <pw> --password-confirm <pw>` | Reset account password |
| `reset-email --email <current> --new-email <new> --email-confirm <new>` | Change account email |
| `create-tenant --email <e> --name <name> [--language en-US]` | Create workspace + account |

#### Database and System

| Command | Description |
|---------|-------------|
| `upgrade-db` | Run database migrations |
| `reset-encrypt-key-pair` | Reset workspace encryption keys (invalidates all stored LLM credentials) |
| `convert-to-agent-apps` | Convert legacy Agent Assistant apps to Agent App type |
| `fix-app-site-missing` | Repair apps with missing site records |

#### Vector Database

| Command | Description |
|---------|-------------|
| `vdb-migrate [--scope all]` | Migrate vectors between backends |
| `add-qdrant-index [--field metadata.doc_id]` | Add index to Qdrant collections |
| `old-metadata-migration` | Migrate legacy metadata format |

#### Data Retention

| Command | Description |
|---------|-------------|
| `clean-workflow-runs --before-days 30 [--dry-run]` | Delete old workflow runs |
| `archive-workflow-runs --before-days 90 [--dry-run]` | Archive runs to S3 |
| `restore-workflow-runs --tenant-ids <id> [--run-id <id>]` | Restore archived runs |
| `delete-archived-workflow-runs --tenant-ids <id> [--dry-run]` | Delete archived runs from DB |
| `clean-expired-messages [--dry-run]` | Clean old messages |
| `export-app-messages --app-id <id> --end-before <date>` | Export messages to JSONL.GZ |
| `clear-free-plan-tenant-expired-logs --days 30` | Clear expired tenant logs |
| `cleanup_orphaned_draft_variables [--dry-run]` | Remove orphaned draft variables |

#### Storage

| Command | Description |
|---------|-------------|
| `clear-orphaned-file-records [-f]` | Remove DB records for missing files |
| `remove-orphaned-files-on-storage [-f]` | Delete storage files without DB records |
| `file-usage [--file-id <id>] [--key <key>] [--json]` | Query file reference locations |
| `migrate-oss [--source opendal] [--dry-run]` | Migrate files between storage backends |

#### Plugins

| Command | Description |
|---------|-------------|
| `migrate-data-for-plugin` | Migrate data for plugin system |
| `extract-plugins [--output_file plugins.jsonl]` | Extract plugin definitions |
| `install-plugins [--input_file plugins.jsonl --workers 100]` | Batch install plugins |
| `install-rag-pipeline-plugins [--workers 100]` | Install RAG pipeline plugins |
| `setup-system-tool-oauth-client --provider <name> --client-params <json>` | Configure tool OAuth |
| `setup-system-trigger-oauth-client --provider <name> --client-params <json>` | Configure trigger OAuth |
| `setup-datasource-oauth-client --provider <name> --client-params <json>` | Configure datasource OAuth |

### API Endpoints -- Quick Reference

#### Service API (`/v1/*`)

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/chat-messages` | Send chat message |
| POST | `/v1/completion-messages` | Send completion request |
| POST | `/v1/workflows/run` | Run a workflow |
| GET | `/v1/workflows/run/{id}` | Get workflow run status |
| POST | `/v1/files/upload` | Upload a file |
| GET | `/v1/messages` | List messages |
| GET | `/v1/conversations` | List conversations |
| DELETE | `/v1/conversations/{id}` | Delete conversation |
| POST | `/v1/conversations/{id}/name` | Rename conversation |
| POST | `/v1/audio-to-text` | Speech to text |
| POST | `/v1/text-to-audio` | Text to speech |
| GET | `/v1/parameters` | Get app parameters |
| GET | `/v1/meta` | Get app metadata |

#### System Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check |
| GET | `/version` | API version |
| GET | `/init` | Setup status |
| GET | `/ping` | Liveness probe |
| GET | `/db-pool-stat` | Database pool statistics |
| GET | `/threads` | Active thread count |

---

_Generated May 2026 from langgenius/dify at 13a9359. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
