# Zapier

> Give a Zap its own email address: a trigger when mail arrives, actions to reply, save drafts and hand off, and a search that waits for a code.

Give a [Zapier](https://zapier.com) Zap an email address of its own. Mail that
arrives starts the Zap; later steps reply, save drafts for a person to send,
hand conversations to people and wait for verification codes. Nobody's own
inbox is connected to Zapier.

**Not on Zapier yet.** The app is built, and run end to end against
production with a test key. It opens as a private beta, by invitation, then
goes to Zapier's review for the public directory. To be in the beta, write
to <hello@pidgeon.ai>. Until then, Zapier's
**Webhooks by Zapier** step can call the [REST API](https://pidgeon.ai/docs/reference.md) with a
key.

| Step                    | Kind    | What it does                                                               | Access |
| ----------------------- | ------- | -------------------------------------------------------------------------- | ------ |
| New Email               | Trigger | Mail arrives at an address                                                 | read   |
| Conversation Handed Off | Trigger | A person takes a conversation, or hands it back                            | read   |
| Send Email              | Action  | Writes to people outside your account: a new conversation                  | send   |
| Reply to Email          | Action  | Writes to people outside your account: answers in the conversation         | send   |
| Save Draft              | Action  | A draft in the address's Drafts folder, threaded as a reply. Sends nothing | draft  |
| Update Conversation     | Action  | Who answers it (a person, the agent, Automation), its status, and why      | draft  |
| Create Address          | Action  | A new address, and who it may write to                                     | admin  |
| Start Waiting for Email | Action  | A cursor to wait from, before the step that sends a code                   | read   |
| Find Email              | Search  | The newest email that matches words, a sender or a folder                  | read   |
| Find Verification Code  | Search  | Waits up to 25 seconds for email with a code, and finds it                 | read   |

## The connection

Make a key at [Developers → API keys](https://pidgeon.ai/app/developers/keys?utm_source=zapier)
and paste it when Zapier asks. Zapier tests it against `GET /v1/me`, and names
the connection after the key and its access, such as `Quotes Zap (draft)`.

**One address and one key per Zap, with the least access it needs.** Whatever
the key reaches, the Zap can reach, and so can any model a later step gives
the mail to. The table above names the access each step needs.

A **test key** (`pgn_test_…`) works the same way, and nothing it sends leaves
Pidgeon ([test mode](https://pidgeon.ai/docs/test-mode.md)). Build the Zap on one, deliver mail to
it with `POST /v1/test/inbound`, and reconnect with a live key when it works.
A test key cannot create addresses.

## The triggers

When you turn a Zap on, its trigger creates a webhook on Pidgeon for the
Zap's own URL, and Zapier keeps its signing secret. When you turn the Zap
off, the webhook is deleted. Each Zap that is on uses one of your plan's
[webhook endpoints](https://pidgeon.ai/docs/limits.md).

**A delivery that is not Pidgeon's starts nothing.** The trigger checks the
signature on the body Zapier received. If it does not match, the trigger reads
the event back from `GET /v1/events/{id}` with your key, and uses that copy:
a forged delivery names an event your key cannot see, and an event older than
Pidgeon's [retries](https://pidgeon.ai/docs/webhooks/retries.md) is a replay. Zapier answers every
request itself, so the sender gets `200` either way; the Zap just does not
run.

### New Email

Choose the address to watch, or none for every address the key reaches.
Pidgeon's webhooks carry ids and metadata, never a body, so the trigger reads
the message and its thread before the Zap runs. Every field a Filter or Paths
step branches on is a plain field:

```json
{
"id": "1a70b25d-…",
"event": "message.received",
"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",
"received_at": "2026-10-25T09:14:03Z",
"spam": false,
"from_self": false,
"bcc_to_this_address": false,
"is_reply": false,
"firewall_verdict": "clean",
"firewall_signals": "",
"auth_dmarc": "pass",
"thread_owner_type": "agent",
"text": "Guten Tag, anbei unser Angebot …",
"reply_text": "Guten Tag, anbei unser Angebot …",
"code": null,
"codes": "",
"attachments": [{ "id": "0b1d4e8c-…", "filename": "angebot.pdf", "content_type": "application/pdf", "size": 48211 }],
"test": false
}
```

- **`text` is clean by default.** [Pidgeon Firewall](https://pidgeon.ai/docs/firewall.md) takes
  out anything hidden or addressed to an AI. `firewall_verdict` is
  `suspicious` when it found something: route that mail to a person, not to a
  model. **Text version → Original** is the text exactly as sent.
- **`from_self` is the one to check before replying.** Mail one of your
  addresses sends to another arrives as ordinary mail.
- **`auth_dmarc` is `pass` only when the From is not forged.** Check it
  before acting on who an email says it is from. Mail from
  `POST /v1/test/inbound` has none, so a DMARC filter stops it while you test.
- **`thread_owner_type`** is `human` once a person has taken the
  conversation. Leave those alone.
- **`id` is the message's id.** Map it into **Email** on Reply to Email, and
  `thread_id` into **Conversation** on Update Conversation.

### Conversation Handed Off

Starts when a person takes a conversation or it is assigned to somebody
(**To a person**), when it is handed back (**Back to the agent**), or either.
The item carries `thread_id`, `owner_type`, `previous_owner_type`, `status`,
`assignee_id`, the `reason` given, and the conversation's address, subject
and sender. Use it to post to Slack or a CRM when a person must act.

## The actions and searches

- **No step asks you to type an id.** **Address**, **Email**,
  **Conversation** and **Assign to** are dropdowns of what the key reaches,
  and each takes a mapped value from an earlier step.
- **A send can be repeated.** Zapier gives a step no run id to make an
  `Idempotency-Key` from, so when Zapier replays a step whose answer was lost,
  the mail can go twice. Turn autoreplay off for a Zap that sends, or send
  with **Save Draft** and a person.
- **Find Verification Code, cursor first.** Put **Start Waiting for Email**
  before the step that makes the code arrive (a form submit, a sign-up), and
  give its **Cursor** to **Find Verification Code** as **After**. Without
  one, it looks back two minutes, so a code that arrived first is still
  found. It waits at most 25 seconds, because Zapier stops a search at 30;
  when nothing comes, it finds nothing, which the Zap can stop on or carry on
  past.
- **A draft waits because nothing sends it.** Give the Zap a `draft` key and
  a person sends from the web inbox or a [mail client](https://pidgeon.ai/docs/mail-clients.md).

## Two Zaps

Zapier lists templates only for apps in its directory, so these are recipes
until the app is.

### New email → Slack

1. **Pidgeon: New Email** on `hello@` (key: `read`).
2. **Filter**: continue only if `from_self` is false, `spam` is false and
   `thread_owner_type` is not `human`.
3. **Slack: Send Channel Message**: `{{from_name}} <{{from}}>: {{subject}}`,
   then `{{reply_text}}`.

For triage, put **Paths** in place of the filter: `firewall_verdict` is
`suspicious` → **Pidgeon: Update Conversation** to a person, with a reason;
otherwise → Slack.

### Form → an agent's address replies

1. **Typeform: New Entry** (or Google Forms, Tally, Webflow).
2. **Pidgeon: Send Email** from `hello@` to the email the form collected,
   with an answer written by a model step or a fixed text (key: `send` on
   that address).
3. When they answer, a second Zap — **Pidgeon: New Email** →
   **Pidgeon: Save Draft** in reply — puts the next answer in Drafts for a
   person to send.

The address writes first here, so it needs **Who it may write to →
Anybody**; an address that may only answer refuses step 2. Keep it to this
one Zap, with a key that reaches nothing else.

## Limits

- A search or action has 30 seconds on Zapier; Find Verification Code waits
  at most 25 of them.
- Each Zap that is on uses one webhook endpoint against your plan.
- The dropdowns show the first 100 addresses, and the newest 25 emails and
  conversations; map or type a value for anything older.
- In the private beta, the app's calls count against the inviting account's
  Zapier rate limit, not yours.
