Search the docs
Find a page, a section, or an endpoint.
Activepieces
A piece: a trigger for mail that arrives, and actions.
Give an Activepieces flow an email address of its own. Mail that arrives starts the flow; the flow replies, hands threads to people and waits for verification codes. Nobody's own inbox is connected to Activepieces, and a model in the flow only ever reads the one message in front of it.
The piece is Pidgeon, a community piece. It has one trigger and six actions:
- Email Received starts a flow when mail arrives at an address.
- Create Address, Send Email, Reply to Email, Update Thread, Wait for Code and Search Emails act on mail, and Custom API Call reaches any other endpoint.
It is not in Activepieces' own catalogue yet, so a platform admin installs it from npm. Activepieces has paused pull requests from outside its team since 16 July 2026, which is how a piece would get into the catalogue. The piece was built and tested inside Activepieces' repository and runs its framework's own HTTP client against production with a test key.
Install
As the platform admin: Platform Admin → Catalogue → Pieces → Install Piece, choose npm, and enter the package name.
@pidgeon-ai/piece-pidgeonOnce it is installed, it appears in the flow builder for every project on the platform. On Activepieces Cloud, installing a piece is part of a paid plan; on a self-hosted Activepieces, the admin who set it up is the platform admin.
The trigger needs Activepieces on a public HTTPS URL, because Pidgeon delivers
webhooks only to HTTPS. Activepieces builds its webhook URLs from
AP_FRONTEND_URL.
The connection
Make a key at Developers → API keys,
then in the flow builder: Pidgeon → Connection → Create connection, and
paste it. Activepieces checks it against GET /v1/me.
One address and one key per flow, with the least access it needs. Whatever the key reaches, the flow can reach, and so can any model you give the flow's output to.
| The flow | Access |
|---|---|
| Only triggers, searches and reads | read |
| Hands threads over | draft |
| Replies or sends | send |
| Creates addresses | admin |
A test key (pgn_test_…) works the same way, and nothing it sends leaves
Pidgeon (test mode). Build the flow on one, deliver mail to
it with POST /v1/test/inbound, and switch the connection to a live key when
it works. A test key cannot create addresses.
The trigger
Email Received watches one address. When the flow is published, it creates its own webhook on Pidgeon for that address and keeps the signing secret in the flow's store, where only the trigger reads it. It checks the signature on every delivery: a request that is not signed by Pidgeon, or is more than five minutes old, starts nothing. When the flow is turned off, the webhook is deleted. A published flow uses one of your plan's webhook endpoints.
Pidgeon's webhooks carry ids and metadata, never a body, so the trigger reads the message and its thread before the flow starts. Every field a Router branches on is a plain field:
{
"event": "message.received",
"message_id": "1a70b25d-…",
"thread_id": "463ec73a-…",
"address": "quotes@acme.pidgeon.email",
"from": "priya@supplier.de",
"from_name": "Priya Raman",
"to": ["quotes@acme.pidgeon.email"],
"cc": [],
"subject": "Angebot 4471",
"spam": false,
"from_self": false,
"bcc_to_this_address": false,
"is_reply": false,
"firewall_verdict": "clean",
"auth_dmarc": "pass",
"thread_owner_type": "agent",
"text": "Guten Tag, anbei unser Angebot …",
"codes": [],
"links": [],
"attachments": [
{ "id": "0b1d4e8c-…", "filename": "angebot.pdf", "content_type": "application/pdf", "size": 48211 }
],
"test": false
}textis clean by default. Pidgeon Firewall takes out anything hidden or addressed to an AI.firewall_verdictissuspiciouswhen it found something: route that mail to a person, not to a model. Original is the text exactly as sent, one dropdown away.from_selfis the one to check before replying. Mail one of your addresses sends to another arrives as ordinary mail.auth_dmarcispassonly when the From is not forged. Check it before acting on who an email says it is from.thread_owner_typeishumanonce a person has taken the thread. Leave those alone.
Test in the trigger reads the newest message at the address, so the data panel shows real fields. Send the address an email first.
The actions
| Action | What it does |
|---|---|
| Create Address | Creates an address. reply_only by default: it can answer mail but not write to a stranger first |
| Send Email | Starts a new conversation. Sends real mail |
| Reply to Email | Answers a message by message_id, in its thread, from the address it arrived at. Sends real mail |
| Update Thread | Hands a thread to a person, so the agent leaves it alone, or back to the agent; or changes its status |
| Wait for Code | Holds until the next mail with a code arrives at an address, up to 280 seconds. code is the code; timed_out is not an error |
| Search Emails | Finds messages by words, sender, address or folder, newest first, up to 100 |
| Custom API Call | Any other endpoint, with the connection's key |
Send Email and Reply to Email send once per step per run: a retry of a failed
step carries the same Idempotency-Key, so Pidgeon does not send the mail
twice.
Read a sign-up code
A flow that signs a service up with its own address:
- Wait for Code on the address, with From set to the service's
sender and After set to
now. - In a branch beside it, the step that signs up with the address.
- The next step reads
{{step_1.code}}.
Wait for Code holds the step for up to 280 seconds, and Activepieces stops a
run after 600 by default (AP_FLOW_TIMEOUT_SECONDS).
Limits
- The trigger watches one address. For several, add a flow for each, with a key for each.
- Each published flow with the trigger uses one webhook endpoint against your plan.
- Search returns at most 100 messages per run.
- Wait for a Code waits at most 280 seconds per run.
- The trigger starts flows for
message.received, and formessage.spamwith Include Spam on. A thread changing hands does not start a flow yet;thread.updatedreaches a Webhook trigger.
The source is in pidgeon-ai/pidgeon-js.