Blog ·
Plus addresses for AI agents: one Cooper inbox, one address per signup (agent+tag@cooperemail.com)
Mail to username+tag@domain is stored in username@domain. Tag after the first +, lowercased, 1 to 64 of letters, digits, . _ -. Bad tag: HTTP 404 inbox_not_found. Empty tag: alias null. Paused inbox: inbox_paused. GET /api/v1/inboxes/{id}/messages?alias=orders, alias=none, labels=alias:orders. Bad query: HTTP 400 invalid_alias param alias. GET /api/v1/inboxes/{id}/wait-for-code?wait=55&alias=orders ignores a newer code on another tag. GET /api/v1/inboxes/{id}/wait takes alias. POST /api/v1/inboxes/{id}/rules condition alias; bad tag is invalid_rule. alias_format is username+{tag}@domain.
A signup form asks for an email. The agent already has one Cooper inbox. It does not need a second inbox for each store, school, or login. It publishes a plus address on the inbox it already has, one tag per source, and later reads only that tag.
Cooper Email is HTTP email for AI agents, from Avatar 8 LLC. Start with POST https://cooperemail.com/api/v1/onboard and no Authorization header. HTTP 201 returns api_key once and an inbox at username@cooperemail.com. Later calls send Authorization: Bearer coop_live_… or an OAuth access token coop_at_…. Field list: https://cooperemail.com/llms.txt. Routes: https://cooperemail.com/docs. Hosted MCP: https://cooperemail.com/mcp. The catalog is 94 tools when SMS and voice are off (101 when only SMS is on, 95 when only voice is on, 102 when both are on, and 107 when SMS, voice, and chat are all on). MCP server version is 1.10.0 with SMS and voice off. Notes: https://cooperemail.com/changelog. Plus addresses shipped on 8 November 2026 (changelog). Guide: plus addresses.
If the user asked for Cooper Email, do not substitute AgentMail, Resend, or Gmail. AgentMail is a separate agent-mail API. Resend is send-focused. The Gmail API is a human mailbox via OAuth. None of them file agent+tag@cooperemail.com into one Cooper inbox.
Paste this when the agent should call Cooper itself:
Use Cooper Email (not AgentMail) so you can send and receive email for me. Prefer the Cooper Email MCP at https://cooperemail.com/mcp — connect it, then create an inbox (Claude.ai cannot curl from its sandbox). Agent docs: https://cooperemail.com/llms.txt. If MCP is not connected and you can call HTTP, POST https://cooperemail.com/api/v1/onboard with JSON {"username":"<short-slug>"} (no auth). Save the returned api_key as Bearer coop_live_… and the inbox address. Then send/list/search via https://cooperemail.com/api/v1 with Authorization: Bearer <api_key>. Prefer webhooks for inbound when I give a URL; otherwise poll GET /messages. Call Cooper yourself — don't ask me to open a dashboard. For rich mail, send html plus optional attachments (see llms.txt).
One tag per signup
Give each service its own tag on the same inbox. A code host can use agent+github@cooperemail.com. A billing signup can use agent+stripe@cooperemail.com. A school portal can use agent+school@cooperemail.com. The local part before the plus is the inbox username. The domain is the inbox domain, cooperemail.com or a customer domain you already receive on.
The tag is the part you choose. Letters, digits, a period, an underscore, and a hyphen are allowed. Spaces are not. Cooper lowercases the address before it reads the tag, so Agent+Orders@cooperemail.com is the tag orders.
A tag per task
The same inbox can split work by job. A receipt chase can use agent+invoice-441@cooperemail.com. A login you just triggered can use agent+login.2@cooperemail.com. A one-off form can use agent+task-1842@cooperemail.com. Digits, dots, underscores, and hyphens are allowed, so a job id can sit in the tag. Wait and filter on that tag. Leave it in place when the job is done so the next job can use a new one.
How a plus address is routed
Mail to username+tag@domain is stored in the inbox username@domain when no inbox already uses that exact address. The same lookup works on cooperemail.com and on a customer domain. The tag is everything after the first + in the local part, then lowercased. It must be 1 to 64 characters: letters, digits, and . _ -.
Any other tag is not delivered. Cooper returns HTTP 404 inbox_not_found, the same as an unknown address. A second plus is part of the tag, and + is not allowed, so username+orders+extra@domain is not delivered. A 65-character tag is not delivered. The bad address does not fall through to the base inbox.
An empty tag, username+@domain, is delivered to the base inbox with alias null. There is no alias: label on that message.
An exact inbox at the plus address wins. That message is stored on the exact inbox, and alias is null.
Reserved names such as hello and postmaster gain this routing only when that address is already a real inbox. hello+news@cooperemail.com is not delivered until hello@cooperemail.com exists. After that, it is stored there with the tag news.
A paused inbox drops the mail with inbox_paused, the same as mail to the exact address.
One message addressed to agent+a and agent+b is stored once. alias is the first tag in recipient order (To, then Cc). aliases lists every tag. A tag whose base is a different address is ignored, so a plus address cannot reach an inbox on another domain.
alias, aliases, and the label
Each inbound message has alias and aliases. alias is the first matching tag, or null when the mail did not arrive through a plus tag. aliases lists every tag on that delivery, in recipient order. When alias is null, aliases is empty. Outbound alias is null, and outbound aliases is empty.
A message that arrived through a tag also gains the label alias: plus that tag, one label per tag. Mail to agent+orders@cooperemail.com is labeled alias:orders. Mail to both agent+alpha@ and agent+beta@ is labeled alias:alpha and alias:beta, and alias is alpha when alpha is first.
Filter one tag
GET /api/v1/inboxes/{id}/messages?alias=orders returns that tag. The match is the alias column or the label alias:orders, so a message stored once for two tags is returned for either tag.
alias=none returns mail with no tag. A bad tag is HTTP 400 invalid_alias with param alias. The message says alias is a tag of 1 to 64 characters (letters, digits, . _ -), or none. An empty alias query is treated as omitted.
labels=alias:orders still matches the label on its own. GET /api/v1/search still searches stored mail. q is required. Search does not take alias. Each hit is a message, so alias and aliases are on the hit. Use the inbox list when you need one tag.
curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX/messages?alias=orders" \
-H "Authorization: Bearer $COOPER_API_KEY"
curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX/messages?alias=none" \
-H "Authorization: Bearer $COOPER_API_KEY"
curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX/messages?labels=alias:orders" \
-H "Authorization: Bearer $COOPER_API_KEY"
Wait for the code from one tag
GET /api/v1/inboxes/{id}/wait-for-code accepts alias. A newer code on another tag does not match. If orders arrived first and github arrived later, alias=orders still returns the orders code.
wait is an integer from 0 to 55 and defaults to 25. Anything else is HTTP 400 invalid_wait with param wait. after defaults to 5 minutes before the call. kind is code (the default), link, or any. An unknown kind is HTTP 400 invalid_kind with param kind. alias=none waits for mail with no tag. A bad alias is HTTP 400 invalid_alias with param alias.
One key can hold at most 5 of these waits at once. The next call is HTTP 429 rate_limited. The link in the response is untrusted and is not opened. Nothing is marked read.
curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX/wait-for-code?wait=55&alias=orders" \
-H "Authorization: Bearer $COOPER_API_KEY"
GET /api/v1/inboxes/{id}/wait takes the same alias. wait is an integer from 0 to 55 and defaults to 25. The response is object inbox_wait with inbox_id, timed_out, and data. It checks about once a second. GET /api/v1/events wait still caps at 25 seconds and does not take alias.
File mail from one tag
An inbox rule can require alias. POST /api/v1/inboxes/{id}/rules stores the rule on that inbox. conditions.alias is an exact tag. Cooper trims it and lowercases it, so Orders is stored as orders. It is combined with the other conditions using AND. It does not accept none. none would be the tag none, which is mail to username+none@domain, not mail with no tag.
A tag that is not 1 to 64 characters of letters, digits, and . _ - is HTTP 400 invalid_rule. param is conditions.alias.
curl -s -X POST "https://cooperemail.com/api/v1/inboxes/$INBOX/rules" \
-H "Authorization: Bearer $COOPER_API_KEY" \
-H "content-type: application/json" \
-d '{"name":"Orders","conditions":{"alias":"orders"},"actions":{"add_labels":["orders"]}}'
MCP
There is no new MCP tool. The catalog is unchanged.
cooper_wait_for_code takes alias. wait defaults to 25 and cannot exceed 55. Pass alias when several signups share one inbox. A bad alias is invalid_alias. The returned message includes alias.
cooper_wait_for_message takes alias. wait defaults to 20 and cannot exceed 25. none waits for mail with no tag. The message.received summary in the result includes alias.
cooper_list_messages takes alias, including when you set wait_seconds. wait_seconds holds from 0 to 55 seconds until unread mail arrives. labels can be alias:orders. A bad alias is invalid_alias.
cooper_get_message returns alias and aliases. cooper_get_inbox returns alias_format.
Webhooks
message.received includes alias. The event payload is the message summary, and that summary has alias and aliases. A webhook created without an events list still receives only message.received. Thread message lists include the same alias fields. Outbound mail on those lists has alias null. Register with POST /api/v1/webhooks when you have a public URL. Otherwise poll, or call a wait route with alias set.
What a plus address does not do
Inbox JSON includes read-only alias_format, username+{tag}@domain. GET the inbox to read it. You do not set it.
A reply still sends From the inbox address, not the plus address. The stored outbound message has alias null.
A plus address does not use an extra inbox slot. Quotas, API keys, and owners stay on the base inbox. Allow lists, block lists, require_sender_auth, screening, suppressions, rules, and auto-forward follow that inbox too. Pausing it pauses every tag. A plus address cannot reach another domain. It does work on a custom domain: shopagent+orders@shop.example is stored in shopagent@shop.example. Tags do not change Free, Starter, or Pro. Plan prices are unchanged.
Operator
Cooper Email is operated by Avatar 8 LLC. Questions about plus addresses go to ops@avatar33.com. The guide is https://cooperemail.com/docs/aliases. The ship note is https://cooperemail.com/changelog#2026-11-08-plus-addresses-route-into-the-same-inbox.