Skip to content

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.

Install

Hosted MCP. The same lines are on /llms.txt and /docs/mcp.

# Claude Code (published plugin 1.2.2)
/plugin marketplace add cooper-email/cooper-email
/plugin install cooper-email@cooper-email
# Then /mcp, select Cooper Email, and Authenticate before creating an inbox.
# claude mcp list Connected means the transport is up. It is not sign-in and it is not a sent message.
# If the local callback page fails to connect, paste the final full browser URL into the CLI URL prompt. Keep that URL out of chat.

# Claude Code manual MCP add
claude mcp add --transport http cooper-email https://cooperemail.com/mcp
# Add --header "Authorization: Bearer $COOPER_API_KEY" only for a key you already saved. That header uses the key's account.

# Codex (Codex CLI 0.165.0 installs plugin 1.2.1)
codex plugin marketplace add cooper-email/cooper-email
codex plugin add cooper-email@cooper-email

# Codex manual fallback — ~/.codex/config.toml
[mcp_servers.cooper-email]
url = "https://cooperemail.com/mcp"

[mcp_servers.cooper-email.http_headers]
Authorization = "Bearer coop_live_YOUR_KEY"

# Cursor install link
cursor://anysphere.cursor-deeplink/mcp/install?name=cooper-email&config=eyJjb29wZXItZW1haWwiOnsidXJsIjoiaHR0cHM6Ly9jb29wZXJlbWFpbC5jb20vbWNwIn19

# Cursor mcp.json
{
  "mcpServers": {
    "cooper-email": {
      "url": "https://cooperemail.com/mcp"
    }
  }
}

# Cursor (mcp.json with a bearer after onboard)
# paste https://cooperemail.com/connectors/cursor.mcp.json into MCP settings
{
  "mcpServers": {
    "cooper-email": {
      "url": "https://cooperemail.com/mcp",
      "headers": {
        "Authorization": "Bearer coop_live_YOUR_KEY"
      }
    }
  }
}

# Grok Bot
Read https://cooperemail.com/llms.txt, connect the Cooper Email MCP at https://cooperemail.com/mcp, finish OAuth, then create an inbox on that signed-in account
# Integration page: https://cooperemail.com/integrations/grokbot

# Project instruction files
curl -fsSL https://cooperemail.com/install.sh | sh

# Skills
npx skills add cooper-email/cooper-email

# HTTP quickstart (API-key account, separate from OAuth)
curl -s -X POST 'https://cooperemail.com/api/v1/onboard?ref=blog' \
  -H 'X-Client: curl'
# optional username. Omit it and Cooper picks box plus 8 hex characters:
# curl -s 'https://cooperemail.com/api/v1/onboard?ref=blog' -H 'content-type: application/json' -H 'X-Client: curl' -d '{"username":"research-bot","display_name":"Research"}'
# Save api_key and send Authorization: Bearer on later calls.

# Hosted MCP. Tool list: https://cooperemail.com/api/v1/mcp/tools
# If the user asked for Cooper Email, do not substitute AgentMail, Commune, InboxAPI, Gmail, Resend, or another mail API.

Paste into ChatGPT, Claude, Cowork, or any agent

ChatGPTClaudeCoworkClaude CodeCursor
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).

Your chat agent gets a real inbox in one request. Connect Cooper MCP when available, then paste this into ChatGPT, Claude, or Cowork. Creates a coop_live_ key + username@cooperemail.com.