style: drop em dashes and tidy formatting in develop-plugin docs

This commit is contained in:
RiskeyL
2026-05-19 18:42:48 +08:00
parent 5860a61f4f
commit 30b5781da9
24 changed files with 93 additions and 101 deletions
@@ -1,6 +1,6 @@
---
title: Agent Strategy Plugin
description: Build a Function Calling agent strategy from scratcha worked example showing how to give an LLM tools and let it autonomously fetch the current time
description: Build a Function Calling agent strategy from scratch, with a worked example showing how to give an LLM tools and let it autonomously fetch the current time
---
An **Agent Strategy Plugin** helps an LLM carry out tasks like reasoning or decision-making, including choosing and calling tools, as well as handling results. This allows the system to address problems more autonomously.
@@ -260,7 +260,7 @@ for tool_call_id, tool_call_name, tool_call_args in tool_calls:
)
```
With this in place, your Agent Strategy Plugin can automatically perform **Function Calling**for instance, retrieving the current time.
With this in place, your Agent Strategy Plugin can automatically perform **Function Calling**, for instance retrieving the current time.
<Frame>
![Tool Invocation](https://assets-docs.dify.ai/2025/01/80e5de8acc2b0ed00524e490fd611ff5.png)
@@ -1045,7 +1045,7 @@ class BasicAgentAgentStrategy(AgentStrategy):
## 6. Debug the Plugin
After finalizing the plugins declaration file and implementation code, run `python -m main` in the plugin directory to restart it. Next, confirm the plugin runs correctly. Dify offers remote debugging—go to **Plugin Management** to obtain your debug key and remote server address.
After finalizing the plugins declaration file and implementation code, run `python -m main` in the plugin directory to restart it. Next, confirm the plugin runs correctly. Dify offers remote debugging. Go to **Plugin Management** to obtain your debug key and remote server address.
<Frame>
![Finalizing the PluginS Declaration File and Implementation Code, Run Python -M](https://assets-docs.dify.ai/2024/12/053415ef127f1f4d6dd85dd3ae79626a.png)
@@ -1081,7 +1081,7 @@ Once everything works, you can package your plugin by running:
dify plugin package ./basic_agent/
```
A file named `basic_agent.difypkg` (matching your plugin name) appears in your current folder—this is your final plugin package.
A file named `basic_agent.difypkg` (matching your plugin name) appears in your current folder. This is your final plugin package.
**Congratulations!** Youve fully developed, tested, and packaged your Agent Strategy Plugin.
@@ -1,6 +1,6 @@
---
title: Data Source Plugin
description: Build a Dify 1.9.0+ datasource plugin that feeds documents into the knowledge pipelinearchitecture, code samples, and debugging steps
description: Build a Dify 1.9.0+ datasource plugin that feeds documents into the knowledge pipeline, with architecture, code samples, and debugging steps
---
Data source plugins are a new type of plugin introduced in Dify 1.9.0. In a knowledge pipeline, they serve as the document data source and the starting point for the entire pipeline.
@@ -15,7 +15,7 @@ description: This guide provides a complete walkthrough for developing a Slack B
**What Youll Learn**:
Gain a solid understanding of how to build a Slack Bot thats powered by AIone that can respond to user questions right inside Slack. If you haven't developed a plugin before, we recommend reading the [Plugin Development Quick Start Guide](/en/develop-plugin/dev-guides-and-walkthroughs/tool-plugin) first.
Gain a solid understanding of how to build a Slack Bot thats powered by AI, one that can respond to user questions right inside Slack. If you haven't developed a plugin before, we recommend reading the [Plugin Development Quick Start Guide](/en/develop-plugin/dev-guides-and-walkthroughs/tool-plugin) first.
## Project Background
@@ -40,7 +40,7 @@ Slack is an open, real-time communication platform with a robust API. Among its
2. **Forward the Message to the Slack Bot Plugin**
The Dify platform triggers the Slack Bot plugin, which relays the details to the Dify applicationsimilar to entering a recipients address in an email system. By setting up a Slack webhook address through Slacks API and entering it in the Slack Bot plugin, you establish this connection. The plugin then processes the Slack request and sends it on to the Dify application, where the LLM analyzes the users input and generates a response.
The Dify platform triggers the Slack Bot plugin, which relays the details to the Dify application, similar to entering a recipients address in an email system. By setting up a Slack webhook address through Slacks API and entering it in the Slack Bot plugin, you establish this connection. The plugin then processes the Slack request and sends it on to the Dify application, where the LLM analyzes the users input and generates a response.
3. **Return the Response to Slack**
@@ -100,7 +100,7 @@ For additional details on reverse-invoking Dify services within a plugin, see [R
This plugin needs to know which Dify app should handle the replies, as well as the Slack App token to authenticate the bots responses. Therefore, youll add these two fields to the plugins form.
Modify the YAML file in the group directoryfor example, `group/slack.yaml`. The forms filename is determined by the info you provided when creating the plugin, so adjust it accordingly.
Modify the YAML file in the group directory (for example, `group/slack.yaml`). The forms filename is determined by the info you provided when creating the plugin, so adjust it accordingly.
**Sample Code**:
@@ -322,7 +322,7 @@ In your code, `self.session.app.chat.invoke` is used to call the Dify applicatio
## 5. Package the Plugin (Optional)
Once you confirm that the plugin works correctly, you can package and name it via the following command. After it runs, youll find a `slack_bot.difypkg` file in the current directoryyour final plugin package. For detailed packaging steps, refer to [Package as a Local File and Share](/en/develop-plugin/publishing/marketplace-listing/release-by-file).
Once you confirm that the plugin works correctly, you can package and name it via the following command. After it runs, youll find a `slack_bot.difypkg` file in the current directory: your final plugin package. For detailed packaging steps, refer to [Package as a Local File and Share](/en/develop-plugin/publishing/marketplace-listing/release-by-file).
```bash
# Replace ./slack_bot with your actual plugin project path.
@@ -506,7 +506,7 @@ Here are some interesting ways to extend this plugin:
<Accordion title="Technical insights">
The core challenge in document conversion is maintaining formatting and structure. The approach used in this plugin first converts markdown to HTML (an intermediate format), then processes that HTML into the target format.
This two-step process provides flexibilityyou could extend it to support additional formats by simply adding new output modules that work with the HTML representation.
This two-step process provides flexibility: you could extend it to support additional formats by simply adding new output modules that work with the HTML representation.
For PDF generation, WeasyPrint was chosen because it offers high-quality PDF rendering with CSS support. For Word documents, python-docx provides granular control over document structure.
</Accordion>
@@ -24,14 +24,14 @@ sequenceDiagram
participant User
rect rgb(235, 245, 255)
Note over Admin,Dify: Flow 1 One-time OAuth client setup
Note over Admin,Dify: Flow 1: One-time OAuth client setup
Admin->>Service: Register OAuth app
Service-->>Admin: client_id + client_secret
Admin->>Dify: Configure plugin OAuth client
end
rect rgb(245, 255, 235)
Note over User,Service: Flow 2 Per-user authorization
Note over User,Service: Flow 2: Per-user authorization
User->>Dify: Click "Authorize"
Dify->>Service: Redirect to consent screen
User->>Service: Approve
@@ -199,7 +199,6 @@ def _oauth_get_authorization_url(self, redirect_uri: str, system_credentials: Ma
return f"{self._AUTH_URL}?{urllib.parse.urlencode(params)}"
```
```python _oauth_get_credentials expandable
def _oauth_get_credentials(
self, redirect_uri: str, system_credentials: Mapping[str, Any], request: Request
@@ -275,7 +274,6 @@ def _oauth_get_credentials(
raise ToolProviderOAuthError(f"Failed to exchange authorization code: {str(e)}")
```
```python _oauth_refresh_credentials
def _oauth_refresh_credentials(
self, redirect_uri: str, system_credentials: Mapping[str, Any], credentials: Mapping[str, Any]
@@ -39,7 +39,7 @@ This article will use `Google Search` as an example to demonstrate how to quickl
- Dify plugin scaffolding tool
- Python environment (version 3.12)
For detailed instructions on how to prepare the plugin development scaffolding tool, please refer to [Initializing Development Tools](/en/develop-plugin/getting-started/cli). If you are developing a plugin for the first time, it is recommended to read [Dify Plugin Development: Hello World Guide](/en/develop-plugin/dev-guides-and-walkthroughs/tool-plugin) first.
For detailed instructions on how to prepare the plugin development scaffolding tool, please refer to [Initializing Development Tools](/en/develop-plugin/getting-started/cli).
## Create a New Project
@@ -29,7 +29,7 @@ To handle webhook events from different platforms in a unified way, Dify defines
- **Subscription**: Webhook-based event dispatch requires **registering Dify's network address on a third-party platform's developer console as the target server. In Dify, this configuration process is called a *Subscription*.**
- **Event**: A platform may send multiple types of eventssuch as *email received*, *email deleted*, or *email marked as read*all of which are pushed to the registered address. A trigger plugin can handle multiple event types, with each event corresponding to a plugin trigger node in a Dify workflow.
- **Event**: A platform may send multiple types of events (such as *email received*, *email deleted*, or *email marked as read*), all of which are pushed to the registered address. A trigger plugin can handle multiple event types, with each event corresponding to a plugin trigger node in a Dify workflow.
## Plugin Development
@@ -208,7 +208,7 @@ Taking the Issue event as an example, you can define the event and its implement
### Event Filtering
To filter out certain eventsfor example, to focus only on Issue events with a specific labelyou can add `parameters` to the event definition in `issues.yaml`. Then, in the `_on_event` method, you can throw an `EventIgnoreError` exception to filter out events that do not meet the configured criteria.
To filter out certain events (for example, to focus only on Issue events with a specific label), you can add `parameters` to the event definition in `issues.yaml`. Then, in the `_on_event` method, you can throw an `EventIgnoreError` exception to filter out events that do not meet the configured criteria.
<Tabs>
<Tab title="issues.yaml">
@@ -17,7 +17,7 @@ description: This document details how to integrate custom models into Dify, usi
A **custom model** refers to an LLM that you deploy or configure on your own. This document uses the [Xinference model](https://inference.readthedocs.io/en/latest/) as an example to demonstrate how to integrate a custom model into your **model plugin**.
By default, a custom model automatically includes two parametersits **model type** and **model name**and does not require additional definitions in the provider YAML file.
By default, a custom model automatically includes two parameters (its **model type** and **model name**) and does not require additional definitions in the provider YAML file.
You do not need to implement `validate_provider_credential` in your provider configuration file. During runtime, based on the users choice of model type or model name, Dify automatically calls the corresponding model layers `validate_credentials` method to verify credentials.
@@ -137,7 +137,7 @@ Once youve defined these parameters, the YAML configuration for your custom m
Since Xinference supports llm, rerank, speech2text, and tts, you should create corresponding directories under /models, each containing its respective feature code.
Below is an example for an llm-type model. Youd create a file named llm.py, then define a classsuch as XinferenceAILargeLanguageModelthat extends \_\_base.large\_language\_model.LargeLanguageModel. This class should include:
Below is an example for an llm-type model. Youd create a file named llm.py, then define a class (such as XinferenceAILargeLanguageModel) that extends \_\_base.large\_language\_model.LargeLanguageModel. This class should include:
* **LLM Invocation**
@@ -22,13 +22,13 @@ For example, for `LLM` type models, it also needs to include `completion_params`
## Invoke LLM
### **Entry Point**
**Entry Point**
```python
self.session.model.llm
```
### **Endpoint**
**Endpoint**
```python
def invoke(
@@ -44,7 +44,7 @@ For example, for `LLM` type models, it also needs to include `completion_params`
Please note that if the model you are invoking does not have `tool_call` capability, the `tools` passed here will not take effect.
### **Use Case**
**Use Case**
If you want to invoke OpenAI's `gpt-4o-mini` model within a `Tool`, please refer to the following example code:
@@ -22,7 +22,7 @@ A model plugin describes itself with two entities: a **Provider** (this page's [
## Quick Decision
<CardGroup cols={2}>
<Card title="Authentication onlypredefined models" icon="key">
<Card title="Authentication only: predefined models" icon="key">
User pastes an API key, gets your full model list. Set `configurate_methods: [predefined-model]` and define each model's AIModelEntity in YAML.
</Card>
<Card title="User-supplied models" icon="user-pen">
@@ -833,7 +833,6 @@ Most text-to-speech APIs require you to specify a voice along with the model. Co
Long text inputs may need to be chunked for better speech synthesis quality. Consider implementing text preprocessing to handle punctuation, numbers, and special characters properly.
</Warning>
### Moderation Implementation
<Info>
@@ -37,7 +37,7 @@ your_plugin/
## Recommended Structure
Keep READMEs conciseunder ~500 words. The Marketplace detail page surfaces the README directly, so optimize it for someone evaluating whether to install:
Keep READMEs concise, under ~500 words. The Marketplace detail page surfaces the README directly, so optimize it for someone evaluating whether to install:
```markdown
# Plugin Name
@@ -66,7 +66,7 @@ Briefly describe what data the plugin sends to third parties. Link to your
PRIVACY.md.
```
Avoid putting full API reference or development notes in the READMEthose belong in source comments or a separate docs site. The Marketplace audience is end users picking a plugin, not contributors reading your codebase.
Avoid putting full API reference or development notes in the README; those belong in source comments or a separate docs site. The Marketplace audience is end users picking a plugin, not contributors reading your codebase.
## How the Marketplace Picks a Language
@@ -7,7 +7,7 @@ dimensions:
standard_title: Plugin info by Manifest
language: en
title: Manifest
description: YAML schema for the plugin manifestdeclares name, author, runtime, resources, permissions, and which tools/models/endpoints the plugin ships
description: YAML schema for the plugin manifest that declares name, author, runtime, resources, permissions, and which tools/models/endpoints the plugin ships
---
The Manifest is a YAML-compliant file that defines the most basic information of a **plugin**, including but not limited to the plugin name, author, included tools, models, etc. For the overall architecture of the plugin, please refer to [Basic Concepts of Plugin Development](/en/develop-plugin/getting-started/getting-started-dify-plugin) and [Developer Cheatsheet](/en/develop-plugin/dev-guides-and-walkthroughs/cheatsheet).
@@ -7,7 +7,7 @@ dimensions:
standard_title: Remote Debug a Plugin
language: en
title: Plugin Debugging
description: Run your in-development plugin locally and attach it to a live Dify workspace for end-to-end testingno packaging required
description: Run your in-development plugin locally and attach it to a live Dify workspace for end-to-end testing, no packaging required
---
Remote debugging is the fastest way to iterate on a plugin. You run the plugin process on your laptop, and Dify treats it as if it were installed in the workspace. Saves, edits, and restarts take effect immediately.
@@ -28,10 +28,10 @@ Open the **Plugins** page in Dify and click the debug icon (the small bug icon n
<Tabs>
<Tab title="Dify Cloud">
The host is something like `debug-plugin.dify.dev:5003`. The key is unique to your workspaceanyone with the key can attach a plugin, so do not share it.
The host is something like `debug-plugin.dify.dev:5003`. The key is unique to your workspace; anyone with the key can attach a plugin, so do not share it.
</Tab>
<Tab title="Self-hosted">
The host defaults to `127.0.0.1:5003` and is configured by `PLUGIN_REMOTE_INSTALLING_HOST` / `PLUGIN_REMOTE_INSTALLING_PORT` in the plugin daemon's environment. Make sure port `5003` is reachable from your dev machine—if Dify runs in Docker on the same host, use the daemon container's bridge IP (or expose the port).
The host defaults to `127.0.0.1:5003` and is configured by `PLUGIN_REMOTE_INSTALLING_HOST` / `PLUGIN_REMOTE_INSTALLING_PORT` in the plugin daemon's environment. Make sure port `5003` is reachable from your dev machine. If Dify runs in Docker on the same host, use the daemon container's bridge IP (or expose the port).
</Tab>
</Tabs>
@@ -46,7 +46,7 @@ REMOTE_INSTALL_KEY=********-****-****-****-************
```
<Note>
`REMOTE_INSTALL_URL` is the combined `host:port` from Step 1not two separate variables.
`REMOTE_INSTALL_URL` is the combined `host:port` from Step 1, not two separate variables.
</Note>
## Step 3: Run the Plugin
@@ -69,10 +69,10 @@ Go back to the Dify **Plugins** page. The plugin appears in the list, labelled w
</Frame>
<Check>
The plugin tile shows a debug indicator and your local terminal logs the first registration handshake — you're attached.
The plugin tile shows a debug indicator and your local terminal logs the first registration handshake. You're attached.
</Check>
Trigger the plugin as you would any othercall it from a workflow node, run it as a tool inside an Agent, or hit an endpoint URL. Invocations land on your local process and you can attach a debugger.
Trigger the plugin as you would any other: call it from a workflow node, run it as a tool inside an Agent, or hit an endpoint URL. Invocations land on your local process and you can attach a debugger.
## Troubleshooting
@@ -87,7 +87,7 @@ Trigger the plugin as you would any other—call it from a workflow node, run it
The `author` field in `manifest.yaml` or in `provider/*.yaml` doesn't match a value the daemon accepts. Set it to your GitHub handle, then restart.
</Accordion>
<Accordion title="Changes don't take effect">
The plugin process needs to restart after every editthere's no hot reload. Stop with `Ctrl+C` and rerun `python -m main`.
The plugin process needs to restart after every edit; there's no hot reload. Stop with `Ctrl+C` and rerun `python -m main`.
</Accordion>
</AccordionGroup>
@@ -27,15 +27,15 @@ Dify supports several plugin types, each targeting a different extension point i
<AccordionGroup>
<Accordion title="Can one plugin combine multiple types?">
Yes—a Tool plugin can also expose endpoints. For example, a Discord bot plugin sends messages (tool) and receives webhooks (endpoint) in one package. Model plugins are stricter: they cannot bundle tools or endpoints.
Yes. A Tool plugin can also expose endpoints. For example, a Discord bot plugin sends messages (tool) and receives webhooks (endpoint) in one package. Model plugins are stricter: they cannot bundle tools or endpoints.
</Accordion>
<Accordion title="Tool vs. Agent Strategywhat's the difference?">
A Tool is an individual capability the Agent decides to call (like "search the web"). An Agent Strategy is the *reasoning loop itself*the policy that decides which tools to call, in what order, and when to stop. Build a tool unless you're replacing how Agents think.
<Accordion title="Tool vs. Agent Strategy: what's the difference?">
A Tool is an individual capability the Agent decides to call (like "search the web"). An Agent Strategy is the *reasoning loop itself*: the policy that decides which tools to call, in what order, and when to stop. Build a tool unless you're replacing how Agents think.
</Accordion>
<Accordion title="Datasource vs. Triggerboth pull from external systems?">
<Accordion title="Datasource vs. Trigger: both pull from external systems?">
Datasource brings documents into a Knowledge Base for retrieval. Trigger fires a workflow in real time on an upstream event. Same systems can power both, but the consumption pattern is different: indexing vs. event-driven execution.
</Accordion>
<Accordion title="Extension vs. Toolwhen is a plain HTTP endpoint right?">
<Accordion title="Extension vs. Tool: when is a plain HTTP endpoint right?">
Extensions are for the other direction: an outside service calls *into* Dify. Tools are for Agents/Workflows calling *out* to a service. Build an Extension when your plugin is the entry point, not the action.
</Accordion>
</AccordionGroup>
@@ -132,7 +132,6 @@ cp .env.example .env
Edit the `.env` file to set your plugin's environment variables, such as API keys or other configurations. You can find these variables in the Dify dashboard. Log in to your Dify environment, click the “Plugins” icon in the top right corner, then click the debug icon (or something that looks like a bug). In the pop-up window, copy the “API Key” and “Host Address”. (Please refer to your local corresponding screenshot, which shows the interface for obtaining the key and host address)
```bash
INSTALL_METHOD=remote
REMOTE_INSTALL_URL=debug-plugin.dify.dev:5003
+10 -14
View File
@@ -20,8 +20,8 @@ The `author` field in `manifest.yaml` and any `provider/*.yaml` file must match
Self-hosted Dify enforces signature verification on plugin packages by default. You have two options:
1. **Recommended for production**sign your package. See [Third-Party Signature Verification](/en/develop-plugin/publishing/standards/third-party-signature-verification).
2. **For development and trusted internal use**disable verification. Add `FORCE_VERIFYING_SIGNATURE=false` to `docker/.env`, then restart:
1. **Recommended for production**: sign your package. See [Third-Party Signature Verification](/en/develop-plugin/publishing/standards/third-party-signature-verification).
2. **For development and trusted internal use**: disable verification. Add `FORCE_VERIFYING_SIGNATURE=false` to `docker/.env`, then restart:
```bash
cd docker
@@ -37,7 +37,7 @@ Yes. The plugin daemon rejects packages over a configurable limit (default `50MB
### My plugin needs Python 3.13. Can I change the runtime?
No. Plugins run inside a managed Python 3.12 environment. Pin your dependencies to versions compatible with 3.12—the daemon does not honor a different runtime version in `manifest.yaml`.
No. Plugins run inside a managed Python 3.12 environment. Pin your dependencies to versions compatible with 3.12. The daemon does not honor a different runtime version in `manifest.yaml`.
## Remote debugging
@@ -45,8 +45,6 @@ No. Plugins run inside a managed Python 3.12 environment. Pin your dependencies
Three things to check, in order: (1) the daemon log shows an incoming registration, (2) the `REMOTE_INSTALL_KEY` matches what's shown in the debug dialog, (3) `REMOTE_INSTALL_URL` uses the `host:port` format (not separate variables). See [Plugin Debugging](/en/develop-plugin/features-and-specs/plugin-types/remote-debug-a-plugin) for the full troubleshooting list.
### Do my code changes hot-reload?
No. Stop the plugin process with `Ctrl+C` and rerun `python -m main` after every change.
@@ -67,12 +65,12 @@ The plugin SDK refreshes tokens automatically when they expire, provided your pr
The reviewer runs a 12-check pre-flight on every PR. The most common failures:
- **`author` in `manifest.yaml` contains `langgenius` or `dify`** reserved for first-party plugins; use your own GitHub handle.
- **`dify_plugin` SDK pin is below `0.5.0`** bump the pin in `requirements.txt`.
- **`README.md` contains Chinese characters** move translations to `readme/README_zh_Hans.md` (see [Multilingual README](/en/develop-plugin/features-and-specs/plugin-types/multilingual-readme)).
- **PR title or body contains Chinese** only the bilingual notice line is allowlisted; everything else must be English.
- **Missing `PRIVACY.md` or `_assets/`** both are required, and `PRIVACY.md` must be non-empty.
- **Version is already published** bump `version` in `manifest.yaml` before re-submitting.
- **`author` in `manifest.yaml` contains `langgenius` or `dify`**: reserved for first-party plugins; use your own GitHub handle.
- **`dify_plugin` SDK pin is below `0.5.0`**: bump the pin in `requirements.txt`.
- **`README.md` contains Chinese characters**: move translations to `readme/README_zh_Hans.md` (see [Multilingual README](/en/develop-plugin/features-and-specs/plugin-types/multilingual-readme)).
- **PR title or body contains Chinese**: only the bilingual notice line is allowlisted; everything else must be English.
- **Missing `PRIVACY.md` or `_assets/`**: both are required, and `PRIVACY.md` must be non-empty.
- **Version is already published**: bump `version` in `manifest.yaml` before re-submitting.
The full list of checks lives in [Publish to Dify Marketplace](/en/develop-plugin/publishing/marketplace-listing/release-to-dify-marketplace#reviewer-checklist).
@@ -86,7 +84,7 @@ Bump `version` in `manifest.yaml`, open a new PR against `langgenius/dify-plugin
### Can I monetize my plugin?
Not currently—the Marketplace only accepts free plugins. Future pricing policies will be announced separately.
Not currently. The Marketplace only accepts free plugins. Future pricing policies will be announced separately.
## Storage and limits
@@ -94,8 +92,6 @@ Not currently—the Marketplace only accepts free plugins. Future pricing polici
Self-hosted: container logs of the plugin daemon (`docker compose logs plugin_daemon`). Cloud: visible in the workspace's plugin debug panel during remote debugging only; persisted logs are not exposed in the UI. Use [Plugin Logging](/en/develop-plugin/features-and-specs/plugin-types/plugin-logging) for structured output.
### Is plugin storage scoped per workspace?
Yes. The KV storage API (`self.session.storage`) is isolated per workspace per plugin install. Two workspaces using the same plugin do not share keys. See [Persistent Storage (KV)](/en/develop-plugin/features-and-specs/plugin-types/persistent-storage-kv).
@@ -7,7 +7,7 @@ dimensions:
standard_title: Release by File
language: en
title: Package as Local File and Share
description: Package a Dify plugin into a `.difypkg` file you can install directly or hand off to others no marketplace, no GitHub release required
description: Package a Dify plugin into a `.difypkg` file you can install directly or hand off to others, with no marketplace or GitHub release required
---
A `.difypkg` is the portable form of a Dify plugin. Once you have one, anyone can install it through the Plugins page by uploading the file. This is the right path for **private plugins**, **internal testing**, or **out-of-band distribution** to a small group.
@@ -62,7 +62,7 @@ The `author` field in `manifest.yaml` and every `provider/*.yaml` must match you
</Frame>
<Check>
The plugin is now in the workspace and ready to use in Agents, Workflows, or Tools — no further action required.
The plugin is now in the workspace and ready to use in Agents, Workflows, or Tools. No further action required.
</Check>
<Warning>
@@ -71,7 +71,7 @@ Self-hosted Dify enforces signature verification by default. Unsigned `.difypkg`
## Share the file
Hand the `.difypkg` file to your users directly over email, internal file share, or any channel you control. They install it with the same flow above.
Hand the `.difypkg` file to your users directly, over email, internal file share, or any channel you control. They install it with the same flow above.
If you want broader distribution instead, consider:
@@ -7,7 +7,7 @@ dimensions:
standard_title: Publishing Overview
language: en
title: Publish Plugins
description: Three ways to distribute a Dify plugin Marketplace, GitHub repository, or local file and how to pick the right one
description: Three ways to distribute a Dify plugin (Marketplace, GitHub repository, or local file) and how to pick the right one
---
Dify supports three ways to distribute a plugin. They differ in how much vetting happens up front and how broad an audience the plugin reaches.
@@ -33,13 +33,13 @@ Before publishing, finish development and testing, and read the [Plugin Developm
| | <Icon icon="store" /> Marketplace | <Icon icon="github" /> GitHub | <Icon icon="file-zipper" /> Local File |
| :--- | :--- | :--- | :--- |
| **Audience** | Every Dify user | Anyone with the repo URL | People you give the file to |
| **Review** | Yes 12 automated checks + human review | None | None |
| **Review** | Yes: 12 automated checks + human review | None | None |
| **Install path** | One click from Marketplace UI | Install from GitHub dialog | Upload via Plugins page |
| **Versioning** | New PR per version, automated workflow available | GitHub releases with `.difypkg` asset | Repackage and redistribute |
| **Best for** | Polished, broadly useful plugins | Open-source projects, internal tools shared by URL | Private plugins, internal testing, ad-hoc distribution |
<Tip>
Many developers ship to **GitHub first** for fast iteration, then submit to the **Marketplace** once the plugin is stable. The same `.difypkg` works for all three methods only the distribution channel differs.
Many developers ship to **GitHub first** for fast iteration, then submit to the **Marketplace** once the plugin is stable. The same `.difypkg` works for all three methods; only the distribution channel differs.
</Tip>
## Quick rules of thumb
@@ -7,7 +7,7 @@ dimensions:
standard_title: Release to Dify Marketplace
language: en
title: Publish to Dify Marketplace
description: Submit a plugin to the Dify Marketplace pre-submission checklist, the 12 reviewer checks, the PR flow, and what happens after approval
description: Submit a plugin to the Dify Marketplace. Covers the pre-submission checklist, the 12 reviewer checks, the PR flow, and what happens after approval
---
The Marketplace is the official catalog of community-built and partner-built Dify plugins. Submitting your plugin here puts it one click away from every Dify user.
@@ -18,7 +18,7 @@ If you have not built a plugin yet, start with the [Tool Plugin walkthrough](/en
## Before you submit
The Dify reviewer runs an automated 12-check pre-flight on every PR. Most rejections are mechanical fixing them up front saves a review cycle.
The Dify reviewer runs an automated 12-check pre-flight on every PR. Most rejections are mechanical, and fixing them up front saves a review cycle.
<Tabs>
<Tab title="Project files">
@@ -28,14 +28,14 @@ The Dify reviewer runs an automated 12-check pre-flight on every PR. Most reject
| :--- | :--- |
| `manifest.yaml` | Plugin metadata (name, author, version, etc.) |
| `README.md` | English-only description, setup, usage |
| `PRIVACY.md` | Privacy policy required, non-empty |
| `PRIVACY.md` | Privacy policy (required, non-empty) |
| `_assets/` | Plugin icon and any other static assets |
See [General Specifications](/en/develop-plugin/features-and-specs/plugin-types/general-specifications) for manifest fields and [Privacy Guidelines](/en/develop-plugin/publishing/standards/privacy-protection-guidelines) for the privacy policy.
</Tab>
<Tab title="Manifest rules">
- **Author** in `manifest.yaml` must not contain `langgenius` or `dify` those are reserved for first-party plugins. Use your own GitHub handle.
- **Version** must be a new value — submitting an already-published version is rejected.
- **Author** in `manifest.yaml` must not contain `langgenius` or `dify`; those are reserved for first-party plugins. Use your own GitHub handle.
- **Version** must be a new value. Submitting an already-published version is rejected.
- **Icon** must be an actual icon in `_assets/`, not a leftover template default.
</Tab>
<Tab title="Dependencies">
@@ -45,7 +45,7 @@ The Dify reviewer runs an automated 12-check pre-flight on every PR. Most reject
</Tab>
<Tab title="Language">
- **PR title and body** must be in English. The bilingual notice line `【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)` is the only allowlisted exception.
- **`README.md`** must contain no Chinese characters. Add translations as `readme/README_<lang>.md` instead — see [Multilingual README](/en/develop-plugin/features-and-specs/plugin-types/multilingual-readme).
- **`README.md`** must contain no Chinese characters. Add translations as `readme/README_<lang>.md` instead. See [Multilingual README](/en/develop-plugin/features-and-specs/plugin-types/multilingual-readme).
</Tab>
</Tabs>
@@ -77,7 +77,7 @@ A failing check stops the review and posts a status table with `❌ Fail` rows a
Skim the [Plugin Development Guidelines](/en/develop-plugin/publishing/standards/contributor-covenant-code-of-conduct). Reviewers use them to judge non-mechanical concerns: uniqueness, brand alignment, content quality, IP, and maintenance commitment.
</Step>
<Step title="Write your privacy policy">
Create `PRIVACY.md` in the plugin root (or host it and put the URL in the manifest). Follow [Privacy Guidelines](/en/develop-plugin/publishing/standards/privacy-protection-guidelines) declare what data the plugin and any third-party services it calls collect.
Create `PRIVACY.md` in the plugin root (or host it and put the URL in the manifest). Follow [Privacy Guidelines](/en/develop-plugin/publishing/standards/privacy-protection-guidelines): declare what data the plugin and any third-party services it calls collect.
</Step>
<Step title="Package the plugin">
From the directory above your plugin project:
@@ -95,7 +95,7 @@ A failing check stops the review and posts a status table with `❌ Fail` rows a
Push to your fork, then open a PR against `main` using the repository's PR template. Title and body in English.
</Step>
<Step title="Respond to the review">
The automated checks post first, then a human reviewer follows up. Address feedback by pushing new commits the checks rerun on each push.
The automated checks post first, then a human reviewer follows up. Address feedback by pushing new commits; the checks rerun on each push.
</Step>
</Steps>
@@ -118,7 +118,7 @@ The first review usually lands within a week. If a reviewer doesn't respond in t
</Tip>
<Check>
Once merged to `main`, the plugin appears on [marketplace.dify.ai](https://marketplace.dify.ai/) automatically no separate publishing step.
Once merged to `main`, the plugin appears on [marketplace.dify.ai](https://marketplace.dify.ai/) automatically, with no separate publishing step.
</Check>
## After approval
@@ -140,24 +140,24 @@ While the Marketplace is in public beta, avoid breaking changes to plugins alrea
| **Open, awaiting review** | Within the first ~7 days, no action needed | Wait |
| **Changes requested** | A check failed, or the reviewer left feedback | Push fixes; checks rerun automatically |
| **Stale (14 days idle)** | No response from you in two weeks | Reply on the PR to reset the timer; it can be reopened |
| **Closed (30 days idle)** | Closed for inactivity | Open a fresh PR the closed one can't be reopened |
| **Closed (30 days idle)** | Closed for inactivity | Open a fresh PR; the closed one can't be reopened |
## Frequently asked questions
<AccordionGroup>
<Accordion title="How do I know if my plugin is too similar to an existing one?">
The Marketplace deduplicates by *function*, not by *integration*. A second Google-search plugin that only adds new translations is a duplicate. A Google-search plugin that adds batched queries, better error handling, or a meaningful new capability is fine say so in the PR description.
The Marketplace deduplicates by *function*, not by *integration*. A second Google-search plugin that only adds new translations is a duplicate. A Google-search plugin that adds batched queries, better error handling, or a meaningful new capability is fine; say so in the PR description.
</Accordion>
<Accordion title="My PR was marked stale or closed. What now?">
**Stale** PRs (14 days idle) can be reopened reply on the PR or push a fix to restart the clock. **Closed** PRs (30 days idle) cannot be reopened; fix the feedback and open a fresh PR with the same package.
**Stale** PRs (14 days idle) can be reopened; reply on the PR or push a fix to restart the clock. **Closed** PRs (30 days idle) cannot be reopened; fix the feedback and open a fresh PR with the same package.
</Accordion>
<Accordion title="Can I update plugins during the public beta?">
Yes. Avoid breaking changes add fields rather than rename, deprecate before removing.
Yes. Avoid breaking changes: add fields rather than rename, deprecate before removing.
</Accordion>
<Accordion title="Can I publish a paid plugin?">
Not currently. The Marketplace accepts free plugins only; monetization policies will be announced separately.
</Accordion>
<Accordion title="The PR body needs both English and Chinese for our team — is that allowed?">
<Accordion title="The PR body needs both English and Chinese for our team. Is that allowed?">
No. Only the single allowlisted bilingual notice line is permitted in the PR title/body. Put internal multilingual notes elsewhere (commit messages, internal docs).
</Accordion>
</AccordionGroup>
@@ -7,7 +7,7 @@ dimensions:
standard_title: Release to Individual GitHub Repo
language: en
title: Publish to Individual GitHub Repository
description: Distribute a Dify plugin from your own GitHub repository so users can install it directly via repo URLno marketplace review required
description: Distribute a Dify plugin from your own GitHub repository so users can install it directly via repo URL, with no marketplace review required
---
Publishing through a personal GitHub repository lets you ship a plugin without going through Dify Marketplace review. Users install the plugin by entering your repo URL and selecting a release that contains a `.difypkg` asset.
@@ -101,14 +101,14 @@ Share your repository URL with users. They install it in Dify as follows:
</Steps>
<Check>
The plugin appears on the user's Plugins page within seconds of clicking **Install** Dify pulls the `.difypkg` from your GitHub release on demand.
The plugin appears on the user's Plugins page within seconds of clicking **Install**. Dify pulls the `.difypkg` from your GitHub release on demand.
</Check>
### Troubleshooting
- **`plugin_unique_identifier is not valid`**the `author` field in `manifest.yaml` (and `provider/*.yaml`) doesn't match the GitHub handle that owns the repo. Update it, repackage, and re-release.
- **No versions appear in the install dialog**the release has no `.difypkg` asset attached, or the repo has no releases yet. Open the release on GitHub and confirm the asset is listed under **Assets**.
- **Signature verification error**self-hosted Dify instances may have signature verification enabled. See [Third-Party Signature Verification](/en/develop-plugin/publishing/standards/third-party-signature-verification) to sign your release, or see the [FAQ](/en/develop-plugin/publishing/faq/faq) for the bypass option.
- **`plugin_unique_identifier is not valid`**: the `author` field in `manifest.yaml` (and `provider/*.yaml`) doesn't match the GitHub handle that owns the repo. Update it, repackage, and re-release.
- **No versions appear in the install dialog**: the release has no `.difypkg` asset attached, or the repo has no releases yet. Open the release on GitHub and confirm the asset is listed under **Assets**.
- **Signature verification error**: self-hosted Dify instances may have signature verification enabled. See [Third-Party Signature Verification](/en/develop-plugin/publishing/standards/third-party-signature-verification) to sign your release, or see the [FAQ](/en/develop-plugin/publishing/faq/faq) for the bypass option.
## Related Resources
@@ -7,7 +7,7 @@ dimensions:
standard_title: Plugin Development Guidelines
language: en
title: Plugin Development Guidelines
description: Requirements every Dify Marketplace plugin must meet to pass review covering quality, language, privacy, IP, maintenance, and prohibited content
description: Requirements every Dify Marketplace plugin must meet to pass review, covering quality, language, privacy, IP, maintenance, and prohibited content
---
By submitting a plugin to the Dify Marketplace, you agree to follow the requirements on this page. Meeting them up front gets your plugin through review faster and helps keep the Marketplace useful and trustworthy.
@@ -16,7 +16,7 @@ By submitting a plugin to the Dify Marketplace, you agree to follow the requirem
- **Generative AI focus.** The plugin's core capability should integrate models, tools, or services that meaningfully extend AI applications on Dify.
- **No functional duplicates.** Don't reskin an existing Marketplace plugin. Each plugin should be unique and stand on its own.
- **Meaningful updates.** New versions must add real capability not just metadata bumps.
- **Meaningful updates.** New versions must add real capability, not just metadata bumps.
- **Explain new submissions.** In your PR description, say briefly why the plugin is needed and what gap it fills.
## Plugin checklist
@@ -28,10 +28,10 @@ Before opening the publish PR, confirm each item:
- **Works end-to-end.** Tested via [remote debugging](/en/develop-plugin/features-and-specs/plugin-types/remote-debug-a-plugin); production-ready.
- **README in English** with setup steps, required credentials, and usage. No exaggerated claims, ads, self-promotion, offensive content, real user data in screenshots, or dead links.
- **Clear error messages** for required fields and failure modes.
- **Authentication steps documented in full** no skipped steps.
- **Privacy policy ready** — see [Privacy Guidelines](/en/develop-plugin/publishing/standards/privacy-protection-guidelines).
- **Credentials handled securely** never hardcoded, never exposed.
- **Performance acceptable** — does not noticeably degrade Dify itself.
- **Authentication steps documented in full**, with no skipped steps.
- **Privacy policy ready.** See [Privacy Guidelines](/en/develop-plugin/publishing/standards/privacy-protection-guidelines).
- **Credentials handled securely**, never hardcoded, never exposed.
- **Performance acceptable.** Does not noticeably degrade Dify itself.
## Language
@@ -41,12 +41,12 @@ English is the primary language for every user-facing string: plugin name, descr
| Category | Reason |
|---|---|
| **Prohibited** misleading or malicious behavior | spam, phishing, credential theft, deceiving the review process |
| **Prohibited** offensive content | violence, hate speech, discrimination |
| **Prohibited** financial transactions | payments, asset transfers, token transfers |
| **Restricted** frequent defects | repeated submissions with critical bugs |
| **Restricted** unnecessary splitting | multiple plugins for the same API/auth bundle them instead |
| **Restricted** duplicate submissions | re-submitting essentially the same plugin |
| **Prohibited**: misleading or malicious behavior | spam, phishing, credential theft, deceiving the review process |
| **Prohibited**: offensive content | violence, hate speech, discrimination |
| **Prohibited**: financial transactions | payments, asset transfers, token transfers |
| **Restricted**: frequent defects | repeated submissions with critical bugs |
| **Restricted**: unnecessary splitting | multiple plugins for the same API/auth; bundle them instead |
| **Restricted**: duplicate submissions | re-submitting essentially the same plugin |
Prohibited plugins are rejected. Restricted plugins may be delayed or asked to consolidate.
@@ -84,12 +84,12 @@ Your privacy policy must state:
- How it is used
- What is shared with third parties (with links to their privacy policies)
Plugins that handle sensitive data health, finance, children's information get extra scrutiny. Collecting or transmitting user data without consent is grounds for removal.
Plugins that handle sensitive data (health, finance, children's information) get extra scrutiny. Collecting or transmitting user data without consent is grounds for removal.
## Review and discretion
- Dify may reject or remove plugins that don't meet these requirements or that abuse the review process.
- Reviews aim to land within a reasonable window turnaround depends on submission volume and complexity.
- Reviews aim to land within a reasonable window; turnaround depends on submission volume and complexity.
- Reviewers communicate via the support channel you provide; keep it monitored.
## Related resources
@@ -14,12 +14,12 @@ Every plugin submitted to the Dify Marketplace must be transparent about how it
The whole submission boils down to one question:
> Does your plugin collect or transmit any user personal data directly, or through a third-party service it calls?
> Does your plugin collect or transmit any user personal data, either directly or through a third-party service it calls?
If yes, list the data types and link a privacy policy. If no, say so explicitly.
<Note>
"Personal data" means any information that identifies a specific individual on its own or when combined with other data anything that could locate, contact, or target a unique person.
"Personal data" means any information that identifies a specific individual on its own or when combined with other data: anything that could locate, contact, or target a unique person.
</Note>
## Identify the data your plugin handles
@@ -61,11 +61,11 @@ Before submitting, read the privacy policy of every third-party API the plugin t
## Write the privacy policy
Your policy either a `PRIVACY.md` in the plugin repository or a hosted URL must cover:
Your policy, either a `PRIVACY.md` in the plugin repository or a hosted URL, must cover:
- **What** is collected (from the categories above).
- **How** it is used.
- **Where** it goes including any third parties and links to their policies.
- **Where** it goes, including any third parties and links to their policies.
If you're unsure where to start, look at the privacy policy of any plugin published by the Dify team for a working template.
@@ -90,9 +90,9 @@ The privacy URL goes in the plugin manifest. See [General Specifications](/en/de
- Processing financial information (bank details, credit scores, transactions)
</Accordion>
<Accordion title="What if my plugin collects nothing?">
Say so explicitly in the privacy policy. Reviewers still expect a `PRIVACY.md` file or hosted URL it just needs to state that no user data is collected, stored, or transmitted, including by any third-party services the plugin calls.
Say so explicitly in the privacy policy. Reviewers still expect a `PRIVACY.md` file or hosted URL; it just needs to state that no user data is collected, stored, or transmitted, including by any third-party services the plugin calls.
</Accordion>
<Accordion title="My plugin handles sensitive data what changes?">
<Accordion title="My plugin handles sensitive data: what changes?">
Plugins that touch health, finance, biometrics, or children's data get extra review. Be explicit about each data type, the legal basis for collecting it, retention period, and how users can request deletion.
</Accordion>
</AccordionGroup>
@@ -7,7 +7,7 @@ dimensions:
standard_title: Third-Party Signature Verification
language: en
title: Sign Plugins for Third-Party Signature Verification
description: Enable signed `.difypkg` installation in Dify Community Edition without disabling signature verification entirely — generate keys, sign packages, distribute the public key
description: Enable signed `.difypkg` installation in Dify Community Edition without disabling signature verification entirely. Generate keys, sign packages, distribute the public key
---
<Warning>
@@ -113,7 +113,7 @@ Admins install signed plugins by giving the plugin daemon a list of trusted publ
docker compose up -d
```
Verified installs are now enforced signed `.difypkg` files matching the configured public keys install cleanly; unsigned or mismatched ones are rejected.
Verified installs are now enforced: signed `.difypkg` files matching the configured public keys install cleanly; unsigned or mismatched ones are rejected.
</Step>
</Steps>