Skip to content

Cooper Email is an email API that gives each AI agent its own inbox. Free is $0 and includes 5 inboxes and 5,000 emails a month.

OpenAPI 3.1 · hosted MCP

One POST /api/v1/onboard creates an inbox and a Bearer key.

The Cooper Email API creates an inbox with one POST /api/v1/onboard and returns a Bearer key; everything else is REST under /api/v1 or the hosted MCP at /mcp.

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.

Directories

Available on

Grok Bot uses this MCP server. Give a Grok bot a Cooper inbox.

Works with

Supported through a config file; no directory listing yet.

Claude · Claude Code · Codex · Cursor · Grok Bot

curl -s 'https://cooperemail.com/api/v1/onboard?ref=docs' \
  -H 'content-type: application/json' \
  -H 'X-Client: curl' \
  -d '{"username":"research-bot","display_name":"Research"}'

export COOP_KEY=coop_live_…
export INBOX=inb_…

curl -s https://cooperemail.com/api/v1/inboxes/$INBOX/messages \
  -H "authorization: Bearer $COOP_KEY" \
  -H 'content-type: application/json' \
  -d '{"to":["ada@example.com"],"subject":"Hello","text":"Tuesday works.","client_id":"send-1"}'

Signup is that one POST. There is no captcha and no dashboard step. owner_email is optional. The same path is written for agents in /llms.txt.

Compared honestly

AgentMail is a mature agent-mail API (inboxes, threads, MCP). Published Free is 3,000/mo, 100/day, 3 inboxes, no custom domain. Cooper Free is 5,000/mo and 5 inboxes, with no daily cap and no custom domain. Starter is $12/month. Onboard is one request, with account-wide FTS. Comparison table.

Resend is excellent transactional send. Inbound is a webhook — there is no persistent searchable inbox. Cooper stores mail as a database you can query.

Gmail API cannot create inboxes programmatically and typically OAuths a human mailbox. That is the wrong isolation model for agents.

Create an inbox in one request

Signup-lite: no password. You get a hashed API key and research-bot@cooperemail.com.

Onboard & keys

POST/api/v1/onboardpublicReferencemarkdown

Create an account, API key, and inbox in one call.

POST/api/v1/keyspublicReferencemarkdown

Without Bearer, create an account and a full-access key (the secret is returned once).

GET/api/v1/oauth/grantsBearerReferencemarkdown

List OAuth apps that currently hold access on this account.

GET/api/v1/oauth/grants/:client_idBearerReferencemarkdown

One connected app for this account.

DELETE/api/v1/oauth/grants/:client_idBearerReferencemarkdown

Revoke one connected app.

POST/api/v1/inboxes/:id/keysBearerReferencemarkdown

Mint a key scoped to this inbox (id, username, or email).

GET/api/v1/lifecycle/unsubscribepublicReferencemarkdown

Confirm page for the product-note unsubscribe link.

POST/api/v1/lifecycle/unsubscribepublicReferencemarkdown

Turn weekly product notes off.

Inboxes

GET/api/v1/inboxes?q=support&limit=100BearerReferencemarkdown

List inboxes for the key.

PATCH/api/v1/inboxes/:idBearerReferencemarkdown

Pause or resume an inbox, set display_name (null clears it), merge metadata, set require_sender_auth, or set screening.

POST/api/v1/inboxes/:id/notifyBearerReferencemarkdown

Register a message.received webhook for this inbox.

PUT/api/v1/inboxes/:id/wakeBearerReferencemarkdown

Register or update one webhook that wakes this inbox for task.received, owner.reply, and chat.message.

GET/api/v1/inboxes/:id/waitBearerReferencemarkdown

Hold until unread mail is stored, then return it.

GET/api/v1/inboxes/:id/wait-for-codeBearerReferencemarkdown

Hold until an inbound message with a one-time verification code is stored, then return the code and the message id.

POST/api/v1/workspacesBearerReferencemarkdown

Create a sub-account with its own API key (returned once) and an inbox_quota set by the parent.

GET/api/v1/workspaces/:id/inboxesBearerReferencemarkdown

List inboxes on this sub-account.

DELETE/api/v1/workspaces/:id/inboxes/:inboxIdBearerReferencemarkdown

Delete one inbox on this sub-account.

Messages

POST/api/v1/inboxes/:id/messagesBearerReferencemarkdown

Send Internet mail via Cloudflare Email Sending, falling through to Resend on Cloudflare auth/permission errors, from username@cooperemail.com.

POST/api/v1/inboxes/:id/messages/:msgId/replyBearerReferencemarkdown

Reply on the same thread.

POST/api/v1/inboxes/:id/messages/:msgId/reply-allBearerReferencemarkdown

Same as reply with reply_all set.

POST/api/v1/inboxes/:id/messages/:msgId/forwardBearerReferencemarkdown

Forward on a new thread.

GET/api/v1/inboxes/:id/messagesBearerReferencemarkdown

List messages, newest first unless ascending=true.

GET/api/v1/inboxes/:id/messages/:msgIdBearerReferencemarkdown

Get one message, including safety.

DELETE/api/v1/inboxes/:id/messages/:msgIdBearerReferencemarkdown

Delete one message, its stored raw RFC822, cached attachment text, attachment bytes, and its search row.

POST/api/v1/inboxes/:id/messages/batch-getBearerReferencemarkdown

Fetch 1 to 500 messages by id.

POST/api/v1/inboxes/:id/messages/batch-updateBearerReferencemarkdown

Add or remove the same labels on 1 to 50 messages.

POST/api/v1/inboxes/:id/messages/:msgId/readBearerReferencemarkdown

Mark one message read: removes unread and adds read.

GET/api/v1/inboxes/:id/messages/:msgId/rawBearerReferencemarkdown

Download the message as message/rfc822.

GET/api/v1/inboxes/:id/messages/:msgId/attachments/:attIdBearerReferencemarkdown

Download attachment bytes.

GET/api/v1/inboxes/:id/messages/:msgId/attachments/:attId/textBearerReferencemarkdown

Extract attachment text as text/plain; charset=utf-8.

POST/api/v1/inboxes/:id/messages/:msgId/releaseBearerReferencemarkdown

Release a message that inbound screening held.

Idempotency

GET/api/v1/idempotency-keys/:keyBearerReferencemarkdown

Read one Idempotency-Key on this account.

DELETE/api/v1/idempotency-keys/:keyBearerReferencemarkdown

Release an unknown Idempotency-Key so the same key can be used again.

POST/api/v1/idempotency-keys/:key/resolveBearerReferencemarkdown

Mark an unknown Idempotency-Key done and link a message the agent found.

Threads

GET/api/v1/inboxes/:id/threads?limit=20BearerReferencemarkdown

List conversations in one inbox, newest activity first.

GET/api/v1/threads?inbox_id=&limit=20BearerReferencemarkdown

List conversations across the account.

GET/api/v1/inboxes/:id/threads/:threadId?limit=100BearerReferencemarkdown

One thread plus its messages, oldest first, including extracted_text.

PATCH/api/v1/inboxes/:id/threads/:threadIdBearerReferencemarkdown

Add or remove labels on every message in the thread, and optionally set folder to inbox, archive, spam, or trash.

POST/api/v1/inboxes/:id/threads/moveBearerReferencemarkdown

Move 1 to 100 threads in this inbox to one folder.

DELETE/api/v1/inboxes/:id/threads/:threadIdBearerReferencemarkdown

Delete every message in the thread, plus stored raw RFC822, cached attachment text, attachment bytes, search rows, the thread assignment, and the folder row.

GET/api/v1/inboxes/:id/threads/:threadId/assignmentBearerReferencemarkdown

Read the assignment for one thread.

PUT/api/v1/inboxes/:id/threads/:threadId/assignmentBearerReferencemarkdown

Assign the thread to a handle, or take an exclusive claim.

DELETE/api/v1/inboxes/:id/threads/:threadId/assignment?assignee=billing-agentBearerReferencemarkdown

Release the assignment.

GET/api/v1/inboxes/:id/threads/:threadId/attachments/:attIdBearerReferencemarkdown

Download attachment bytes that belong to a message in this thread.

Inbox rules

GET/api/v1/inboxes/:id/rulesBearerReferencemarkdown

List deterministic rules for this inbox, lowest position first.

PATCH/api/v1/inboxes/:id/rules/:ruleIdBearerReferencemarkdown

Change name, enabled, position, conditions, actions, or stop_processing.

DELETE/api/v1/inboxes/:id/rules/:ruleIdBearerReferencemarkdown

Delete one rule and its auto-reply cooldown rows.

Webhooks & events

POST/api/v1/webhooksBearerReferencemarkdown

Register a URL for message.received, message.held, message.sent, message.blocked, message.bounced, message.complained, task.received, owner.reply, chat.message, domain.verified, draft.created, draft.approved, draft.rejected, draft.sent, account.sending_paused, account.sending_resumed, thread.assigned, and thread.unassigned.

GET/api/v1/event-subscriptionsBearerReferencemarkdown

List MCP event subscriptions for this account.

DELETE/api/v1/event-subscriptions/:idBearerReferencemarkdown

End an MCP event subscription.

POST/api/v1/webhooks/:id/rotate-secretBearerReferencemarkdown

Mint a new signing secret and return it once.

POST/api/v1/webhooks/:id/testBearerReferencemarkdown

Send {type:"webhook.test", data:{webhook_id}} to this hook and wait for the response.

GET/api/v1/webhooks/:id/deliveriesBearerReferencemarkdown

List delivery attempts for one webhook, newest first.

POST/api/v1/webhooks/:id/deliveries/:deliveryId/redeliverBearerReferencemarkdown

Send the stored payload again with the same event id and a new x-cooper-timestamp and signature.

GET/api/v1/inboxes/:id/eventsBearerReferencemarkdown

Same list as GET /api/v1/events, scoped to one inbox.

GET/api/v1/events/streamBearerReferencemarkdown

Server-Sent Events for up to 25 seconds.

POST/api/v1/event-consumersBearerReferencemarkdown

Create or resume a durable event consumer.

GET/api/v1/event-consumers/:idBearerReferencemarkdown

Read one durable event consumer.

GET/api/v1/event-consumers/:id/eventsBearerReferencemarkdown

Events strictly after the committed cursor, oldest first, in the same shape as GET /api/v1/events.

POST/api/v1/event-consumers/:id/commitBearerReferencemarkdown

Advance the committed cursor.

POST/api/v1/event-consumers/:id/resetBearerReferencemarkdown

Move the committed cursor.

Domains

POST/api/v1/domainsBearerReferencemarkdown

Add a custom domain on Starter or Pro and return SPF, DKIM, DMARC, and MX records plus a verify TXT.

GET/api/v1/domains/:idBearerReferencemarkdown

Status for one custom domain: pending or verified, mail, status_detail, warnings, and the required DNS records with per-record status, found, and hint.

GET/api/v1/domains/:id/zone-fileBearerReferencemarkdown

text/plain BIND lines for the records to publish.

Owners & approval

POST/api/v1/updatesBearerReferencemarkdown

Send a progress, done, needs_input, or error note to every verified owner.

GET/api/v1/tasks?status=pendingBearerReferencemarkdown

Poll tasks created when a verified owner or allowlisted sender emails the inbox and DMARC=pass or DKIM=pass aligns with the From domain.

POST/api/v1/tasks/:id/replyBearerReferencemarkdown

Reply in-thread to the human and optionally set status to in_progress or done.

Drafts

GET/PATCH/DELETE/api/v1/inboxes/:id/drafts/:draftIdBearerReferencemarkdown

Read, update, or delete one draft.

GET/api/v1/inboxes/:id/drafts/:draftId/attachments/:attIdBearerReferencemarkdown

Download one draft attachment.

Lists & suppression

GET/POST/api/v1/lists/:direction/:typeBearerReferencemarkdown

Account-wide allow and block lists.

GET/DELETE/api/v1/lists/:direction/:type/:entryBearerReferencemarkdown

Read or remove one account-wide list entry.

GET/POST/api/v1/inboxes/:id/lists/:direction/:typeBearerReferencemarkdown

Allow and block lists for one inbox.

GET/DELETE/api/v1/inboxes/:id/lists/:direction/:type/:entryBearerReferencemarkdown

Read or remove one inbox list entry.

DELETE/api/v1/suppressions/:addressBearerReferencemarkdown

Remove one suppressed address.

Metrics

GET/api/v1/metrics/eventsBearerReferencemarkdown

UTC buckets of message.received, message.sent, message.bounced, and message.complained.

GET/api/v1/metrics/usageBearerReferencemarkdown

Current inbox_count, message_count, thread_count, and storage_bytes.

GET/api/v1/metrics/ratesBearerReferencemarkdown

sent, bounced, complained, bounce_rate, and complaint_rate.

Billing

POST/api/v1/billing/addonsBearerReferencemarkdown

Set extra inbox, custom domain, and email quantities on a Starter or Pro subscription.

SMS

Your agent gets a phone number

Starter and Pro agents can buy a US local number through the API or MCP and receive SMS on that inbox. Each billable number is $3 a month. Pro includes 3 numbers.

  • Phone number: Free includes none.
  • Phone number: up to 3 US numbers, each $3 a month with 100 segments. None are included.
  • Phone number: 3 US numbers and 1,500 segments included, up to 25 numbers. Each extra number is $3 a month with 100 segments.

Buy a number, then wait for an inbound text:

curl -s https://cooperemail.com/api/v1/inboxes/$INBOX_ID/phone-number \
  -H "authorization: Bearer $COOP_KEY" \
  -H "content-type: application/json" \
  -H "idempotency-key: phone-1" \
  -d '{"area_code":"415"}'

curl -s "https://cooperemail.com/api/v1/inboxes/$INBOX_ID/events?types=message.received&wait=25" \
  -H "authorization: Bearer $COOP_KEY"

The events request waits up to 25 seconds for any received message, email or SMS. To wait for a text only, use the MCP tool cooper_wait_for_sms (default 20 seconds, maximum 25).

Phone numbers · SMS docs · Provision reference · Send SMS reference · Phone quickstart.

Voice

A caller hears a short greeting, then a recording. That recording is stored on the inbox as a message with channel voicemail, the caller number, the duration, and the transcript. Place a call from the same number with POST /api/v1/inboxes/{id}/calls or the MCP tool cooper_place_call. Free includes no voice minutes. Starter includes 30 minutes per active number each month. Pro includes 300 minutes, plus 30 for each active number past the 3 included numbers.

curl -s https://cooperemail.com/api/v1/inboxes/$INBOX/calls \
  -H "authorization: Bearer $COOP_KEY" \
  -H 'content-type: application/json' \
  -d '{"to":"+14155550100","say":"Tuesday works.","mode":"say"}'

Voice docs · Place a call · Greeting · Minute packs.

CLI

Shell agents install one Node file and call the same REST API. CLI docs · markdown.

curl -fsSL https://cooperemail.com/cli/cooper.mjs -o cooper.mjs && node cooper.mjs help
npx -y https://cooperemail.com/cli/cooper-cli.tgz help

Names only on this page. Descriptions and input schemas are in Connect Cooper MCP · markdown · GET /api/v1/mcp/tools.

  • cooper_onboard
  • cooper_create_inbox
  • cooper_create_inboxes
  • cooper_list_inboxes
  • cooper_send_message
  • cooper_list_messages
  • cooper_get_message
  • cooper_search
  • cooper_search_messages
  • cooper_inject_inbound
  • cooper_register_webhook
  • cooper_enable_instant_replies
  • cooper_billing_status
  • cooper_upgrade_link
  • cooper_add_owner
  • cooper_list_owners
  • cooper_notify_owner
  • cooper_get_tasks
  • cooper_reply_task
  • cooper_add_domain
  • cooper_create_workspace
  • cooper_list_workspaces
  • cooper_get_workspace
  • cooper_update_workspace
  • cooper_delete_workspace
  • cooper_delete_inbox
  • cooper_update_inbox
  • cooper_update_message
  • cooper_update_messages
  • cooper_list_domains
  • cooper_verify_domain
  • cooper_reply_message
  • cooper_forward_message
  • cooper_wait_for_message
  • cooper_list_events
  • cooper_list_threads
  • cooper_get_thread
  • cooper_search_threads
  • cooper_delete_thread
  • cooper_update_thread
  • cooper_move_threads
  • cooper_assign_thread
  • cooper_unassign_thread
  • cooper_list_list_entries
  • cooper_get_list_entry
  • cooper_create_list_entry
  • cooper_delete_list_entry
  • cooper_get_inbox
  • cooper_confirm_owner
  • cooper_whoami
  • cooper_list_webhooks
  • cooper_update_webhook
  • cooper_delete_webhook
  • cooper_rotate_webhook_secret
  • cooper_test_webhook
  • cooper_list_suppressions
  • cooper_delete_suppression
  • cooper_release_message
  • cooper_get_attachment
  • cooper_get_metrics
  • cooper_get_sending_status
  • cooper_create_draft
  • cooper_get_draft
  • cooper_list_drafts
  • cooper_update_draft
  • cooper_delete_draft
  • cooper_send_draft
  • cooper_wait_for_code
  • cooper_get_owner_preferences
  • cooper_set_owner_preferences
  • cooper_list_oauth_grants
  • cooper_revoke_oauth_grant
  • cooper_list_event_subscriptions
  • cooper_delete_event_subscription
  • cooper_set_addons
  • cooper_poll_events
  • cooper_commit_events
  • cooper_list_event_consumers
  • cooper_delete_event_consumer
  • cooper_list_rules
  • cooper_create_rule
  • cooper_update_rule
  • cooper_delete_rule
  • cooper_test_rule
  • cooper_get_idempotency_key
  • cooper_release_idempotency_key
  • cooper_resolve_idempotency_key

Agents ask

Docs FAQ

Onboard, MCP, webhooks, and verification codes. More answers are on the FAQ.

How does onboard work?

POST /api/v1/onboard with { "username": "research-bot" }. No password, no dashboard click-path, no OAuth. Cooper creates an account, stores a SHA-256 hash of the API key, mints username@cooperemail.com, and returns the plaintext key once. Agents (including Claude Code) can do this unattended. Use client_id on later writes for idempotency. Recommended: add your human owner's email. They'll know this inbox exists, get approvals and the daily digest, and can help recover the account if your API key is lost.

Do you support webhooks?

Yes. POST /api/v1/webhooks registers an HTTPS URL for message.received, message.sent, task.received, and owner.reply. Optional headers (Authorization or X-*, max 5, stored encrypted, redacted on read) are sent with x-cooper-signature. Optional inbox_id limits the hook to one inbox. PATCH and DELETE /api/v1/webhooks/:id update or remove it. GET includes last_delivery_status and last_delivery_at. Inbound Internet mail is Cloudflare Email Routing catch-all → the cooper-inbound-email Worker → POST /api/v1/internal/inbound with a shared secret; Cooper then stores the message and fires the signed webhook. POST /api/v1/inboxes/:id/inbound remains a Bearer test injector.

How does authentication work?

Authorization: Bearer coop_live_… or an OAuth access token from the Cooper authorization server (/.well-known/oauth-authorization-server). Keys and tokens are hashed at rest (SHA-256 only). Missing or unknown credentials return a clear 401 with a stable error.code. Onboard and signup-lite POST /api/v1/keys stay public. Claude/ChatGPT custom connectors should use OAuth Connect rather than pasting a header.

Can an AI agent receive a verification code?

Yes. Create an inbox, enter that address on the sign-up form, then GET /api/v1/inboxes/{id}/wait-for-code. The call holds until an inbound message contains a 4 to 8 digit code (or 123-456) next to a word such as code, OTP, verification, passcode, or PIN. It returns the code and the message id. The body is not returned. wait defaults to 25 seconds and the maximum is 55. MCP: cooper_wait_for_code. Guide: https://cooperemail.com/guides/ai-agent-receive-otp-verification-code-email.

More answers on the FAQ