Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dify.ai/llms.txt

Use this file to discover all available pages before exploring further.

What you’ll build

By the end of this guide, you’ll have created a Dify plugin that:
  • Connects to the Flomo note-taking API
  • Allows users to save notes from AI conversations directly to Flomo
  • Handles authentication and error states properly
  • Is ready for distribution in the Dify Marketplace

Time required

10 minutes

Prerequisites

Basic Python knowledge and a Flomo account

Step 1: Install the Dify CLI and create a project

1

Install Dify CLI

brew tap langgenius/dify
brew install dify
Verify installation:
dify version
2

Initialize a plugin project

Create a new plugin project using:
dify plugin init
Follow the prompts to set up your plugin:
  • Name it “flomo”
  • Select “tool” as the plugin type
  • Complete other required fields
3

Navigate to the project

cd flomo
This will create the basic structure for your plugin with all necessary files.

Step 2: Define your plugin manifest

The manifest.yaml file defines your plugin’s metadata, permissions, and capabilities.
Create a manifest.yaml file:
version: 0.0.4
type: plugin
author: yourname
label:
  en_US: Flomo
  zh_Hans: Flomo 浮墨笔记
created_at: "2023-10-01T00:00:00Z"
icon: icon.png

resource:
  memory: 67108864  # 64MB
  permission:
    storage:
      enabled: false

plugins:
  tools:
    - provider/flomo.yaml

meta:
  version: 0.0.1
  arch:
    - amd64
    - arm64
  runner:
    language: python
    version: 3.12
    entrypoint: main

Step 3: Create the tool definition

A tool plugin uses two YAML files: a provider file that declares credentials and lists the tools, and one tool file per callable tool. See General Specifications for the full schema. Create provider/flomo.yaml:
identity:
  author: yourname
  name: flomo
  label:
    en_US: Flomo Note
    zh_Hans: Flomo 浮墨笔记
  description:
    en_US: Add notes to your Flomo account directly from Dify.
    zh_Hans: 直接从 Dify 添加笔记到您的 Flomo 账户。
  icon: icon.png
credentials_for_provider:
  api_url:
    type: secret-input
    required: true
    label:
      en_US: API URL
      zh_Hans: API URL
    placeholder:
      en_US: https://flomoapp.com/iwh/{token}/{secret}/
    help:
      en_US: Flomo API URL from your Flomo account settings.
      zh_Hans: 从您的 Flomo 账户设置中获取的 API URL。
tools:
  - tools/flomo.yaml
extra:
  python:
    source: provider/flomo.py
Create tools/flomo.yaml:
identity:
  name: flomo
  author: yourname
  label:
    en_US: Save to Flomo
    zh_Hans: 保存到 Flomo
description:
  human:
    en_US: Save the conversation content as a Flomo note.
    zh_Hans: 将对话内容保存为 Flomo 笔记。
  llm: >
    Saves content to the user's Flomo account. Use this tool when the user
    asks to save, capture, or remember the current message. Takes a single
    `content` parameter containing the text to save.
parameters:
  - name: content
    type: string
    required: true
    label:
      en_US: Note content
      zh_Hans: 笔记内容
    human_description:
      en_US: Content to save as a note in Flomo.
      zh_Hans: 要保存为 Flomo 笔记的内容。
    llm_description: The text to save as a Flomo note.
    form: llm
extra:
  python:
    source: tools/flomo.py

Step 4: Implement core utility functions

Create a utility module in utils/flomo_utils.py for API interaction:
import requests

def send_flomo_note(api_url: str, content: str) -> None:
    """
    Send a note to Flomo via the API URL. Raises requests.RequestException on network errors,
    and ValueError on invalid status codes or input.
    """
    api_url = api_url.strip()
    if not api_url:
        raise ValueError("API URL is required and cannot be empty.")
    if not api_url.startswith('https://flomoapp.com/iwh/'):
        raise ValueError(
            "API URL should be in the format: https://flomoapp.com/iwh/{token}/{secret}/"
        )
    if not content:
        raise ValueError("Content cannot be empty.")
    
    headers = {'Content-Type': 'application/json'}
    response = requests.post(api_url, json={"content": content}, headers=headers, timeout=10)
    
    if response.status_code != 200:
        raise ValueError(f"API URL is not valid. Received status code: {response.status_code}")

Step 5: Implement the Tool Provider

The Tool Provider handles credential validation. Create provider/flomo.py:
from typing import Any
from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
import requests
from utils.flomo_utils import send_flomo_note

class FlomoProvider(ToolProvider):
    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
        try:
            api_url = credentials.get('api_url', '').strip()
            # Use utility for validation and sending test note
            send_flomo_note(api_url, "Hello, #flomo https://flomoapp.com")
        except ValueError as e:
            raise ToolProviderCredentialValidationError(str(e))
        except requests.RequestException as e:
            raise ToolProviderCredentialValidationError(f"Connection error: {str(e)}")

Step 6: Implement the Tool

The Tool class handles actual API calls when the user invokes the plugin. Create tools/flomo.py:
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
import requests
from utils.flomo_utils import send_flomo_note

class FlomoTool(Tool):
    def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
        content = tool_parameters.get("content", "")
        api_url = self.runtime.credentials.get("api_url", "")
        
        try:
            send_flomo_note(api_url, content)
        except ValueError as e:
            yield self.create_text_message(str(e))
            return
        except requests.RequestException as e:
            yield self.create_text_message(f"Connection error: {str(e)}")
            return
            
        # Return success message and structured data
        yield self.create_text_message(
            "Note created successfully! Your content has been sent to Flomo."
        )
        yield self.create_json_message({
            "status": "success",
            "content": content,
        })
Always handle exceptions gracefully and return user-friendly error messages. Remember that your plugin represents your brand in the Dify ecosystem.

Step 7: Test your plugin

1

Set up debug environment

Copy the example environment file:
cp .env.example .env
Edit the .env file with your Dify environment details:
INSTALL_METHOD=remote
REMOTE_INSTALL_URL=debug-plugin.dify.dev:5003
REMOTE_INSTALL_KEY=your_debug_key
You can find your debug URL and key in the Dify dashboard: click the Plugins icon in the top-right corner, then click the debug icon. Copy the API Key and Host Address (the host already includes the port).
2

Install dependencies and run

pip install -r requirements.txt
python -m main
Your plugin will connect to your Dify instance in debug mode.
3

Test functionality

In your Dify instance, navigate to plugins and find your debugging plugin (marked as “debugging”). Add your Flomo API credentials and test sending a note.

Step 8: Package and distribute

When you’re ready to share your plugin:
dify plugin package ./
This creates a plugin.difypkg file you can upload to the Dify Marketplace.

FAQ and Troubleshooting

Make sure your .env file is properly configured and you’re using the correct debug key.
Double-check your Flomo API URL format. It should be in the form: https://flomoapp.com/iwh/{token}/{secret}/
Ensure all required files are present and the manifest.yaml structure is valid.

Summary

You’ve built a functioning Dify plugin that connects with an external API service! This same pattern works for integrating with thousands of services - from databases and search engines to productivity tools and custom APIs.

Documentation

Write your README.md in English (en_US) describing functionality, setup, and usage examples

Localization

Create additional README files like readme/README_zh_Hans.md for other languages

Edit this page | Report an issue