Skip to content

Blog ·

Forward an AI agent's inbound email to a verified Gmail, Outlook, or other mailbox

POST /api/v1/inboxes/{id}/forwarding/verifications: HTTP 202, 6-digit code to that address only, not stored in any Cooper inbox, not in the response. POST .../forwarding/confirm. confirm_invalid. confirm_expired after 48 hours or 5 wrong attempts. 5 destinations, HTTP 409 forwarding_limit (technical, not a plan limit). forwarding_target_not_allowed. forwarding_not_verified. 429 rate_limited: 5/hour/IP and 3/day/address. 30 forwards per rolling hour. GET message_read. Writes inbox_update. MCP cooper_list_forwarding, cooper_request_forwarding, cooper_remove_forwarding, cooper_set_forwarding.

An agent inbox can keep the mail, and a person can still read a copy in Gmail, Outlook, or any other mailbox they already open. Auto-forward sends that copy after the destination is verified. A pending address receives nothing.

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. Auto-forward shipped on 7 November 2026 (changelog). Guide: auto-forward. The same page as markdown: https://cooperemail.com/docs/forwarding.md.

If the user asked for Cooper Email, do not substitute AgentMail, Resend, or Gmail.

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

Why the destination must be verified

The agent can name an address. It cannot prove it controls that mailbox. Cooper emails a 6-digit code to the destination only. The digits are not written into any Cooper inbox, and the HTTP response does not include them. The row stores a SHA-256 hash so a later confirm can match. A successful confirm clears the hash. A human reads the code in that mailbox and supplies the digits. An agent that can only read Cooper mail cannot self-confirm an arbitrary address. Nothing is forwarded until confirm succeeds.

The confirmation message is not a link. Subject: Confirm forwarding from plus the inbox address. From is the inbox address. Reply-To is noreply@cooperemail.com. Headers: Auto-Submitted: auto-generated and X-Cooper-Forwarding-Confirm: 1. It is not stored on the inbox and does not count toward the monthly send quota.

Ask for the code

POST /api/v1/inboxes/{id}/forwarding/verifications needs inbox_update. {id} is the inbox id, the username, or the email. The body is {email} and may include client_id (1 to 128 characters). Cooper trims the address, lowercases it, and removes trailing dots. A bad shape, or more than 254 characters, is HTTP 400 invalid_email. A bad client_id length is HTTP 400 invalid_client_id.

A new or still-pending address returns HTTP 202 and object forwarding_destination. An address that is already verified returns HTTP 200 and does not send mail. The same client_id returns the original row and does not send another code. The same client_id with a different email is HTTP 409 client_id_conflict.

curl -s -X POST "https://cooperemail.com/api/v1/inboxes/$INBOX/forwarding/verifications" \
  -H "Authorization: Bearer $COOPER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"archive@example.com","client_id":"archive-gmail"}'

The inbox itself, cooperemail.com and its subdomains, and any address that is already a Cooper inbox are HTTP 400 forwarding_target_not_allowed. At most 5 destinations, pending and verified together. The next new address is HTTP 409 forwarding_limit, checked before the confirmation message is sent. That cap is technical and does not change Free, Starter, or Pro. Repeating an address already on the list does not take a new slot.

Confirmation email is 5 per hour per IP and 3 per day per address, on fixed windows, then HTTP 429 rate_limited. A paused inbox is HTTP 403 inbox_paused and sends nothing. Another account's inbox is HTTP 404 inbox_not_found.

Confirm the code

POST /api/v1/inboxes/{id}/forwarding/confirm needs inbox_update and {email, code}. A value that is not 6 digits is HTTP 400 confirm_invalid and does not burn an attempt. A wrong 6-digit code is the same error and adds one attempt. The code expires 48 hours after it was sent. After five wrong attempts the next call is HTTP 400 confirm_expired. Requesting verification again replaces the hash, resets the attempts, and starts another 48 hours. Confirm calls are also limited to 30 per hour per IP, then HTTP 429 rate_limited. An unknown address is HTTP 404 forwarding_destination_not_found. An address that is already verified returns HTTP 200 unchanged.

curl -s -X POST "https://cooperemail.com/api/v1/inboxes/$INBOX/forwarding/confirm" \
  -H "Authorization: Bearer $COOPER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"archive@example.com","code":"123456"}'

123456 above is a placeholder. Use the digits from the confirmation message.

List, replace, and remove

GET /api/v1/inboxes/{id}/forwarding needs message_read. The body is object inbox_forwarding. destinations lists pending and verified rows, oldest first. unverified is the pending rows again. Each row has id (an ifd_ id), email, status, verified_at, created_at, updated_at, expires_at, and client_id. expires_at is set only while pending. The code is absent.

curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX/forwarding" \
  -H "Authorization: Bearer $COOPER_API_KEY"

PUT /api/v1/inboxes/{id}/forwarding needs inbox_update and {emails}. It replaces the verified list. Every address must already be verified, or the response is HTTP 400 forwarding_not_verified. Pending rows stay and still count toward 5. Duplicates are kept once. An empty array removes every verified destination. Overflow is HTTP 409 forwarding_limit. PUT does not send a code.

curl -s -X PUT "https://cooperemail.com/api/v1/inboxes/$INBOX/forwarding" \
  -H "Authorization: Bearer $COOPER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"emails":["archive@example.com"]}'

DELETE /api/v1/inboxes/{id}/forwarding/{destinationId} needs inbox_update. destinationId is the ifd_ id or the email. The response is {id, email, deleted: true}. An unknown id or email is HTTP 404 forwarding_destination_not_found. Delete works for a pending row and a verified row.

curl -s -X DELETE "https://cooperemail.com/api/v1/inboxes/$INBOX/forwarding/$DESTINATION" \
  -H "Authorization: Bearer $COOPER_API_KEY"

Mail that arrived before the address was verified is not copied later. There is no backfill.

What the copy looks like

Rules run first, then auto-forward. Each verified destination gets one copy, composed like POST forward: Fwd: unless the subject already starts with fwd: or fw:, plus the original text, attachments, and the inbox signature when one is set. The outbound client_id joins autofwd_, the destination id, and the message id, so that pair is not sent twice. The copy sets Auto-Submitted: auto-forwarded, X-Cooper-Auto-Forward to the inbox id, and X-Cooper-Forwarded: 1.

A sent copy counts toward the monthly email quota. Quota, suppressions, and sending health are checked first. A suppressed destination, or an account paused for bounce or complaint rate, skips that copy. The inbound message stays. message.received still fires when the mail is not held. GET /api/v1/search still searches the stored inbound message. The outbound copy is a separate row.

What is not forwarded

Auto-forward skips outbound mail, and any message whose channel is set to something other than email (SMS, voicemail, and voice). It skips labels held, bounce, complaint, and auto. Releasing a held message fires message.received and does not forward it. It skips Auto-Submitted set to anything other than no, and it skips mail that already has X-Cooper-Auto-Forward or X-Cooper-Forwarded. Spam and trash are skipped. A rule that files the thread there runs first. Inbox and archive can still be copied. A destination equal to this inbox is skipped.

An inbox sends at most 30 of these forwards per rolling hour. The count is outbound rows plus approval drafts whose client_id starts with autofwd in the last hour. Past 30, remaining destinations and later messages in that hour are skipped. They are not queued and they are not retried. Destinations are tried oldest first.

Approval mode

When send_mode is approval, the copy is a pending draft. It is not sent until a verified owner approves it. The draft counts toward the 30-per-hour cap. It does not count toward the monthly send quota until that send succeeds. Approval mode with no verified owner skips the copy and keeps the inbound message. Draft approval: drafts and owner approval. Owners and tasks are a different loop: human in the loop.

MCP

Four tools call the same routes. None of them accept or return the code. There is no MCP tool for confirm. The human confirms with the REST confirm route.

cooper_list_forwarding needs message_read. Pass inbox_id. It returns destinations and unverified.

cooper_request_forwarding needs inbox_update. Pass inbox_id and email. client_id is optional. Status pending is the HTTP 202 case. Status verified is the HTTP 200 case.

cooper_remove_forwarding needs inbox_update. Pass inbox_id and destination_id, the ifd_ id or the email.

cooper_set_forwarding needs inbox_update. Pass inbox_id and emails. Each address must already be verified. Pending rows stay. An address that is not verified is HTTP 400 forwarding_not_verified.

Inbox rules are a different forward

actions.forward_to accepts only another inbox on this account, or a verified owner of this inbox. Anything else is HTTP 400 forward_target_not_allowed. The rule forwards only matching mail, and creating that action needs message_send.

Auto-forward is the path for a verified external address such as archive@example.com. It does not use forward_to. It considers every new inbound message that passes the skips above. The rule cap of 20 and the rule send cap of 30 per rolling hour are separate numbers. Inbox rules.

Plus addresses use the base inbox

Mail to username+tag@domain is stored in username@domain. Auto-forward then runs on that base inbox, so every tag follows the same verified destinations. A plus address does not have its own forwarding list. Plus addresses.

FAQ

Can the agent confirm an address by itself?

Only with digits a human read in the destination mailbox. The code is not in the Cooper inbox, the API response, or any MCP result.

Does a pending address get a copy?

No. Only a verified row receives copies.

What happens after 30 forwards in an hour?

Later copies that hour are skipped and are not queued. The inbound message stays.

Which permission does the key need?

GET uses message_read. Verification, confirm, PUT, and DELETE use inbox_update. Sending the copy does not require message_send on the key. A forward_to rule still does.

Operator

Cooper Email is operated by Avatar 8 LLC. Questions about auto-forward go to ops@avatar33.com. The guide is https://cooperemail.com/docs/forwarding and https://cooperemail.com/docs/forwarding.md. The ship note is https://cooperemail.com/changelog#2026-11-07-forward-inbound-mail-to-verified-external-addresses.

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.