Give an agent its own email address — disposable inboxes for sign-ups and verification codes, persistent ones that send, reply, and forward, with a person approving first messages to new recipients.
Agent mail is rolling out and is not yet available to every organization. When
it is on for yours, the mail__* tools appear in tools/list; until then they
are absent. If it is ever paused for your organization, the tools disappear and
your persistent inboxes stop being charged; they keep their addresses and keep
receiving mail until it is back on.
Mail is a CoreSpeed-hosted capability: your agent gets real email addresses of its own, with nothing to sign up for and no mailbox credentials to hold. There are two kinds of inbox:
- Disposable — lives up to 7 days (
ttl_hours≤ 168), receive-only, and deletes itself when it expires. For sign-ups, verification codes and one-off replies. - Persistent — lives until you delete it (or for up to a year with
ttl_hours), and can also send, reply, and forward.
An agent's inboxes are its own; pass shared: true to make one visible to
every agent and member of the organization. A member sees all of the
organization's inboxes.
Tool names follow mail__<action>. The wire names and exact parameters come
from tools/list; the tables below are the map.
Tools
Inboxes
| Tool | Does |
|---|---|
mail__create_inbox | Create an inbox and return its address. ttl_hours picks disposable or persistent; allow_senders limits who can write to it (inbound only). |
mail__list_inboxes | The inboxes you can use. |
mail__get_inbox | One inbox: address, kind, whether it can send, expiry. |
mail__update_inbox | Rename it or replace its sender allowlist. |
mail__delete_inbox | Delete it. Irreversible — the address is never issued again. |
Reading
| Tool | Returns |
|---|---|
mail__list_messages | Messages in one inbox, newest first, with filters (text, sender, subject, unread, label, time). Each summary carries any verification codes found. |
mail__search_messages | Full-text search across every inbox you can see. |
mail__read_message | One message in full: cleaned text, extracted codes, links classified as verify / login / reset / unsubscribe, and attachments. |
mail__get_thread | A whole conversation. |
mail__wait_for_message | Waits up to 50 seconds for a matching message and returns it — the way to catch a code right after signing up. |
mail__get_attachment | Stores an attachment as a private file in your Drive under tmp/mail/ (7 days; drive__move keeps it) that feeds Media understanding, connector uploads, and mail__send. |
mail__update_message | Mark read or unread; add or remove labels. |
mail__list_labels | An inbox's labels with counts. |
Sending
| Tool | Does |
|---|---|
mail__check_recipients | Without sending, tells you how each address would be treated and whether a send would need approval. |
mail__send | A new message to up to 10 recipients, with optional attachments. |
mail__reply | A reply in the thread — to the sender, or everyone with reply_all. |
mail__forward | Forward a message, with its attachments by default. |
Attachments are passed as a Drive path or file id — an attachment you stored, a Media output, an upload — or a public https URL — up to 10 files per message. The whole message, attachments included, must fit in 5 MiB once encoded for sending. Disposable inboxes cannot send.
Catching a verification code
mail__create_inbox { "ttl_hours": 1 } → inbox address
… the agent signs up with that address …
mail__wait_for_message { "inbox_id": "inb_…", "from": "@example.com" }
→ message with codes[]Without since, a matching unread message that already arrived is returned at
once, so the order of sign-up and wait does not matter. The returned message is
marked read; calling again waits for the next one. A wait that times out
returns message: null — call it again to keep waiting.
First messages to new people need a person
A send to someone new outside your organization — not a member, not on your
organization's allowlist, and not already written to from this inbox — waits for
a person's approval, whatever your approval policy says. That includes the first
reply to someone who wrote in and every forward to a new address: receiving
mail from an address never makes it trusted, because a "From" can be forged. The call returns
Approval required with an approval_id; the agent calls
manage__approval_wait with it at once, and the message is sent the moment it
is approved. The card goes to the agent's owner like every
Smart Approvals card. If Smart Approvals is not available to
your organization, such a send is refused with approval_required and nothing
is sent. Mail to your organization's own members,
to addresses on the organization allowlist (set by an admin), and to addresses
this inbox has already written to needs no approval. An inbox's allow_senders
only filters what it receives; it never lets a send skip approval.
Sending limits also protect every inbox's reputation: at most 10 recipients per message, an hourly cap per inbox, daily caps that grow with the inbox's age and with the time since your organization first sent, and an automatic pause if too many messages bounce or are marked as spam.
Mail content is untrusted
Everything in a message — subject, body, sender name, links, even the codes —
was written by whoever sent it. Every result carrying mail opens with a notice
that says so and is marked untrusted_content: true (and
_meta["io.corespeed/untrusted-content"]), so agents treat it as data and never
as instructions, and says that sender names and addresses are not verified. When
a message explicitly failed an email authentication check (SPF, DKIM, DMARC) the
result says so too, because a forged "From" is how phishing aimed at agents
begins.
Pricing and holds
| What | Charge |
|---|---|
| Disposable inbox | 5 credits, once, when created |
| Persistent inbox | 300 credits per 30 days while it exists, the first period when created |
| Sending | 1 credit per recipient (to + cc + bcc) |
| Receiving, reading, searching, waiting | Free |
Pass idempotency_key on mail__create_inbox and the send tools when you
might retry: a repeat returns the first result and is not charged again. A
failed or refused call never charges. Charges land in the organization ledger
as mail/inbox-disposable, mail/inbox-persistent and mail/send; see
Billing & credits. A persistent inbox keeps being charged each
period until it is deleted — deleting it stops the charge, and the current
period is not refunded. An organization billing hold stops mail calls before
they run, as it does every metered tool, while its inboxes keep receiving.
mail__delete_inbox still runs during a hold, so you can always stop a
recurring charge.
Controls
Mail appears as Agent mail in
Dashboard → Tools. Turning it off hides every
mail__* tool from tools/list for that member; see
Capability controls.