Search the docs

Find a page, a section, or an endpoint.

Dify

A plugin: send, reply, search and wait for a code.

Give a Dify agent or workflow an email address of its own. It sends, replies in the thread, searches its mail, waits for a verification code and hands a thread to a person. Nobody's own inbox is connected to Dify.

The plugin is Pidgeon, a tool plugin built on Pidgeon's Python client. It has six tools:

ToolWhat it does
Create AddressCreates an address. It can only answer people who wrote first unless you choose Anybody
Send EmailStarts a new conversation from one of your addresses. Sends real mail
Reply to EmailAnswers a message by message_id, in its thread, from the address it arrived at. Sends real mail
Search EmailsFinds messages by words, sender, address or folder, newest first, up to 100
Wait for CodeHolds until mail with a verification code arrives, up to 280 seconds, and returns code
Update ThreadHands a thread to a person, so the agent leaves it alone, or back to the agent; or sets its status

It is not on the Dify Marketplace yet. Until it is, install the package on a self-hosted Dify, as below. Each tool was run against production with a test key, through the plugin SDK's own tool runtime.

Install

From the Marketplace, once it is listed: Plugins → Explore Marketplace, search for Pidgeon, and install.

Before then, on a self-hosted Dify, build the package from the source with Dify's plugin CLI (dify, from dify-plugin-daemon):

bash
git clone https://github.com/pidgeon-ai/pidgeon-python.git
cd pidgeon-python/dify
dify plugin package pidgeon -o pidgeon-0.0.1.difypkg

Then choose Plugins → Install plugin → Local package file. Dify refuses a package it has not signed unless the plugin daemon runs with

bash
FORCE_VERIFYING_SIGNATURE=false

Dify Cloud does not offer that setting, so on Cloud the plugin arrives with the Marketplace listing.

The key

Make a key at Developers → API keys, then in Dify open Tools → Pidgeon → Authorize and paste it. Dify checks it against GET /v1/me.

One address and one key per app, with the least access it needs. Whatever the key reaches, the app's model can reach.

The appAccess
Searches and waits for codesRead only (read)
Hands threads overRead and draft (draft)
Replies or sendsRead and send (send)
Creates addressesFull access (admin)

A test key (pgn_test_…) works the same way, and nothing it sends leaves Pidgeon (test mode). Build the app on one, deliver mail to it with POST /v1/test/inbound, and switch to a live key when it works. A test key cannot create addresses.

In an agent

Add the tools under Tools in an Agent app. Each tool tells the model what it does and when to use it. A refusal comes back as Pidgeon's own message, such as "This address can only answer people who wrote first. (not_permitted)", so the agent can act on it rather than retry.

Text in an email is written by whoever sent it. Wait for Code returns the text with anything hidden or addressed to an AI taken out, and firewall_verdict is suspicious when Pidgeon Firewall found something. Keep Send Email off an agent that reads mail from strangers, or give it a key with Read and draft and let a person send.

In a workflow

Every tool returns a json output. Wait for Code's is flat, so an IF/ELSE node branches on a field by name:

json
{
"code": "482910",
"message_id": "1a70b25d-…",
"thread_id": "463ec73a-…",
"address": "signup@acme.pidgeon.email",
"from": "no-reply@github.com",
"from_name": "GitHub",
"to": ["signup@acme.pidgeon.email"],
"cc": [],
"subject": "Your verification code",
"received_at": "2026-10-26T09:14:03Z",
"spam": false,
"bcc_to_this_address": false,
"firewall_verdict": "clean",
"auth_dmarc": "pass",
"text": "Your code is 482910 …",
"codes": ["482910"],
"links": [],
"attachments": [],
"test": false,
"timed_out": false,
"cursor": "…"
}
  • timed_out is not an error. Nothing arriving in the time you gave is an answer: branch on it.
  • A code that arrived up to two minutes before the call still counts, so the step that triggers the email can run first.
  • Set From to the service's sender. Only mail from it that passes DMARC is returned, so another sender cannot hand the agent a code.

Limits

  • The plugin has tools, not a trigger: mail arriving does not start an app.
  • Search returns at most 100 messages per call.
  • Wait for Code waits at most 280 seconds per call.

The source is in pidgeon-ai/pidgeon-python.