Mailbox connections¶
An agent can watch a real inbox for new mail and react on its own judgment: reply, send a WhatsApp or Telegram message, place a call, or take no action and just log why. It's the same menu of reactions a trigger has, but decided by the agent's own reasoning rather than a fixed rule. Each agent can have at most one mailbox, the email equivalent of its phone number.
Two ways to connect a mailbox:
- A real IMAP and SMTP account. Works with any mail provider. AiFlow polls it on an interval, and a reply goes out over SMTP using that mailbox's own credentials.
- Resend's own inbound receiving (recommended). Resend delivers new
mail as a webhook instead of AiFlow polling for it, so there's no
password to store, and a reply goes out through the same Resend account
already configured for outbound mail on this deployment. This requires
RESEND_API_KEYandRESEND_WEBHOOK_SECRETset on the deployment. See Credentials for the full setup walkthrough: the receiving subdomain, the MX record, and registering the webhook.
A newly connected mailbox never reacts to whatever's already sitting in the inbox, only to mail that arrives from the moment it's connected onward. Connecting an existing support inbox with years of history in it won't trigger a reply to every old message.
Memory across a conversation¶
Turn on cross_session_lookback_enabled (see Agents) and the
agent recalls its last conversation with a sender the moment a new email
from that same address arrives, using the same recap mechanism phone and
widget sessions already rely on. A follow-up email ("thanks, one more
question...") gets a short summary of what was already discussed folded
into the agent's context before it starts, instead of a blank slate; a
sender it's never seen before gets none. Each inbound email is still its
own independent agent run: this carries a recap forward, it isn't live
multi-turn memory within one exchange.
A reply sent with reply_to_email also carries proper In-Reply-To and
References headers back to the message it's answering, so it threads
under the original in the recipient's own mail client (Gmail, Outlook, and
so on) instead of showing up as a disconnected new email.
Admin role required
GET needs any logged-in admin. PUT and DELETE need Owner or Admin.
Field reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
provider |
string | No | "imap_smtp" |
"imap_smtp" or "resend_inbound". |
enabled |
boolean | No | true |
Whether the agent is currently watching this mailbox. |
from_display_name |
string or null |
No | null |
The display name a reply is sent as, e.g. "Acme Support". |
imap_host/imap_username |
string | Yes, for imap_smtp |
None | The mailbox's IMAP server and login. |
imap_password |
string | Yes, for imap_smtp, on create |
None | Write-only. Omit on an update to keep the currently stored password. |
imap_port/imap_use_ssl |
integer/boolean | No | 993/true |
|
folder |
string | No | "INBOX" |
The IMAP folder to watch. |
poll_interval_seconds |
integer | No | 60 |
How often this mailbox is checked. imap_smtp only, ignored for resend_inbound. |
smtp_host/smtp_username |
string | Yes, for imap_smtp |
None | Where a reply is sent from. |
smtp_password |
string | Yes, for imap_smtp, on create |
None | Write-only, same omit-to-keep behavior as imap_password. |
smtp_port/smtp_use_tls |
integer/boolean | No | 587/true |
|
resend_receiving_address |
string | Yes, for resend_inbound |
None | The address mail actually arrives at, e.g. support@inbound.your-domain.com. |
Response shape¶
GET and PUT both return this shape. Password fields are write-only and
never echoed back.
{
"id": 1,
"agent_id": 1,
"provider": "resend_inbound",
"enabled": true,
"from_display_name": "Acme Support",
"imap_host": null,
"imap_port": 993,
"imap_use_ssl": true,
"imap_username": null,
"folder": "INBOX",
"poll_interval_seconds": 60,
"smtp_host": null,
"smtp_port": 587,
"smtp_use_tls": true,
"smtp_username": null,
"resend_receiving_address": "support@inbound.your-domain.com",
"last_seen_uid": null,
"last_polled_at": null,
"created_at": "2026-01-15T10:31:00Z"
}
last_seen_uid and last_polled_at are imap_smtp-only bookkeeping,
always null for a resend_inbound connection since that provider has no
polling loop to track.
Configure a mailbox¶
Creates the connection if none exists yet, or updates the same one in place otherwise. Since there's at most one per agent, this single endpoint covers both cases.
import httpx
response = httpx.put(
"https://api.your-domain.com/api/v1/agents/1/mailbox-connection",
headers={"Authorization": f"Bearer {admin_token}"},
json={
"provider": "resend_inbound",
"resend_receiving_address": "support@inbound.your-domain.com",
"from_display_name": "Acme Support",
},
)
response.raise_for_status()
connection = response.json()
const response = await fetch(
"https://api.your-domain.com/api/v1/agents/1/mailbox-connection",
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${adminToken}`,
},
body: JSON.stringify({
provider: "resend_inbound",
resend_receiving_address: "support@inbound.your-domain.com",
from_display_name: "Acme Support",
}),
},
);
const connection = await response.json();
An IMAP and SMTP connection looks the same, with the provider-specific fields swapped in:
{
"provider": "imap_smtp",
"imap_host": "imap.example.com",
"imap_username": "support@example.com",
"imap_password": "the-real-password",
"smtp_host": "smtp.example.com",
"smtp_username": "support@example.com",
"smtp_password": "the-real-password",
"from_display_name": "Acme Support"
}
Get the current connection¶
Returns 404 if this agent has no mailbox connected yet.
Delete a connection¶
Returns 204 No Content, even if none existed, since deleting is
idempotent. Set "enabled": false via PUT instead if you want to
temporarily stop the agent watching its inbox without losing the
configuration.
The reply_to_email tool¶
Enable it on the agent alongside a connected mailbox, and it can reply to
an inbound message through that mailbox's own address. It's distinct from
the platform-wide send_email tool. See Tools for how any
built-in tool is turned on for an agent.
Next¶
- Tools: the full built-in catalog, including
reply_to_email. - Triggers: the same reply, WhatsApp, Telegram, call, or silent-action menu, decided by a fixed rule instead of the agent's own judgment.
- Agents & channels: how a mailbox fits alongside the widget, phone, and events as a way to reach an agent.