Skip to main content

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 the 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 the other required fields
3

Navigate to the project

cd flomo
The project contains 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, open the Plugins page and find your 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. The 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