# Email channel

Inbound email arrives via your mail provider's webhook; replies are sent over the
channel's **own SMTP**. There is no global SMTP — each email channel configures its
own, or it can receive but not reply.

## Configuration

Channels → **New channel** → Kind = **email**.

| Field                                 | What it is                                                                                                                                      |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Default agent**                     | Agent that answers.                                                                                                                             |
| **Identity**                          | The address users write to — must match the email's `To`, e.g. `bot@you.com`.                                                                   |
| **Inbound webhook secret** (required) | Authenticates the provider's webhook calls. Supports `{{ variables }}`. Inbound mail is **rejected** if this is unset — otherwise anyone who learns the URL could feed messages to your agent. |
| **SMTP**                              | host, port, secure (TLS), user, pass, from. Required to send replies. Supports `{{ variables }}` — store the password as a **secure variable**. |

`secure` = on for port 465, off for 587 (STARTTLS). `from` defaults to the channel identity.

Save, then copy the URLs from the **Endpoint** panel.

## Endpoints

Two body shapes, depending on your provider:

```
POST /email/<workspaceId>/<channelId>/inbound        # parsed fields
POST /email/<workspaceId>/<channelId>/inbound/raw    # raw MIME in { from, to, raw }
```

- **`/inbound`** — for providers that post parsed fields: **SendGrid Inbound Parse**, **Mailgun Routes**, **Postmark** (multipart or JSON).
- **`/inbound/raw`** — for a forwarder that posts the raw MIME message, e.g. a **Cloudflare Email Worker** sending `{ from, to, raw }`.
- **Secret:** pass it as `?secret=…` or the `X-Webhook-Secret` header. Requests without it get a 401.
- **Legacy:** `POST /email/inbound` resolves the channel by recipient address (kept for existing setups).

History is kept per correspondent (thread key = sender's address), and replies thread via `In-Reply-To`.

## Provider setup (examples)

- **SendGrid Inbound Parse:** point the MX host at SendGrid, set the Inbound Parse destination URL to your `/inbound` endpoint (append `?secret=…`).
- **Cloudflare Email Worker:** forward the message as JSON `{ from, to, raw }` to the `/inbound/raw` endpoint with the `X-Webhook-Secret` header.

## Notes

- The webhook is acknowledged immediately (200); the agent runs and the reply is sent in the background, so the provider isn't kept waiting on the LLM.
- **Auto-replies are ignored.** Mail carrying `Auto-Submitted` (anything but `no`), `Precedence: bulk|list|junk`, `List-Id`/`List-Unsubscribe`, or a vendor auto-reply header is dropped without running the agent, as is mail from the channel's own address. Answering an autoresponder would loop: our reply triggers theirs, which arrives as new inbound. Our own replies carry `Auto-Submitted: auto-replied` so the other side can do the same.
- An email channel with no SMTP **receives** messages but logs a warning and won't reply.
