Blog ·
How anyone reports a leaked Cooper Email API key (and Cooper revokes it)
POST /api/v1/keys/report-leak is public: no API key, account, or signed-in owner. Body is one object or an array of at most 20. key (token alias) is required, trimmed, at most 200 characters. source_url (url alias) is optional http or https, at most 2048. reporter is optional plain text of at most 200 characters and is not stored. Valid bodies always return HTTP 202 {received}. received is the item count, not revocations. A live match then fails with HTTP 401 api_key_revoked. 30 requests per hour per IP returns HTTP 429 rate_limited. Event key.leak_reported. No MCP tool.
A coop_live_ key showed up in a paste, a log, or a repository. The finder does not need the Cooper account and does not need to be signed in. They report the key. Cooper revokes it.
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 90 tools when SMS and voice are off (97 when only SMS is on, 91 when only voice is on, 98 when both are on, and 103 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. This route shipped on 5 November 2026 (changelog). Guide: report a leaked key. Reference: POST /api/v1/keys/report-leak.
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 revoke a Cooper key.
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).
Anyone who finds a key can report it
POST /api/v1/keys/report-leak is public. It does not need an API key, an account, or a signed-in owner. A person, a secret scanner, or the developer on another machine can send the key.
Cooper does not currently have a partnership with a scanning vendor or with GitHub secret scanning. A scanner can still call this route. The call is the same for a person and for a script.
A finding that is not a leaked key goes to ops@avatar33.com. The policy page is https://cooperemail.com/security. This route only revokes a coop_live_ key.
What a Cooper key looks like
A live key is the prefix coop_live_ plus 32 base64url characters. Secret scanners can match:
coop_live_[A-Za-z0-9_-]{32}
Keys are stored as a SHA-256 hash. The report route hashes the string you send, compares that hash, and does not write the raw key. The response does not echo it.
A string that does not match is accepted and ignored. A batch may mix Cooper keys with other tokens. Those other tokens do not change the status, and they still count in received. Use the placeholder coop_live_… in examples. Put a real key only in the report body.
The request body
The body is one JSON object, or an array of at most 20 objects.
key is required. Cooper trims it. After trimming it must be non-empty and at most 200 characters. token is an alias of key.
source_url is optional. It must be http or https, and at most 2048 characters. url is an alias of source_url. Omit it when you have no page to cite. An empty string is no URL.
reporter is optional plain text of at most 200 characters. Cooper checks it and does not store it. The owner email and the event omit it.
Unknown fields are ignored, including type and source.
curl -sS -X POST https://cooperemail.com/api/v1/keys/report-leak \
-H "content-type: application/json" \
-d '{"key":"coop_live_…","source_url":"https://example.com/paste"}'
curl -sS -X POST https://cooperemail.com/api/v1/keys/report-leak \
-H "content-type: application/json" \
-d '[{"key":"coop_live_…","url":"https://example.com/paste"},{"token":"not-a-cooper-key"}]'
When Cooper rejects the body
Invalid JSON is HTTP 400 invalid_json. The message is Request body must be valid JSON.
A missing key, a key that is not a string, a blank key, or a key longer than 200 characters is HTTP 400 invalid_key. param is key.
A bad source_url (not a string, longer than 2048 characters, or not http or https) is HTTP 400 invalid_source_url. param is source_url.
A reporter that is not plain text, or longer than 200 characters, is HTTP 400 invalid_reporter. param is reporter.
More than 20 items is HTTP 400 too_many_items. param is body. The message is Send at most 20 keys in one report. A body that is neither an object nor an array is HTTP 400 invalid_body, also with param body.
The error object has type, code, and message. param is set when the failure names a field. On these 400s, type is invalid_request.
{
"error": {
"type": "invalid_request",
"code": "invalid_key",
"message": "key is required.",
"param": "key"
}
}
Thirty requests an hour
The limit is 30 requests per hour for each client IP. The next request is HTTP 429 rate_limited. type is rate_limit. The message says leak reports are limited to 30 per hour for this IP.
The response includes a Retry-After header, plus retry_after_seconds and resets_at in the JSON. retry_after_seconds is that header as an integer, at least 1. resets_at is that many seconds later, in UTC. The window counts requests, not keys in the array. One POST of 20 objects is one request.
HTTP 202 either way
A valid body is always HTTP 202 {received:N}. received is how many items were in the body. It is not how many keys were revoked.
An unknown key, a key that is already revoked, and a string that is not a Cooper key all return that same 202. The body never includes the key, an account id, or whether a key was valid. 202 means Cooper accepted the report.
{ "received": 1 }
What a match does
A hash that matches a key which is not already revoked revokes that key immediately. The next REST call and the next MCP call with it return HTTP 401 api_key_revoked. The message is This API key has been revoked.
Other keys on the account keep working.
Cooper sets revoked_at and leak_reported_at to the time of the report. revoked_reason is leak_reported. leak_source_url is the reported URL, or null when the report omitted one.
DELETE /api/v1/keys/{id} is the signed-in revoke. It sets revoked_reason to manual. A key that is already revoked keeps its time and its reason. A later leak report does not change them. A row revoked before this field existed stays null.
GET /api/v1/keys and GET /api/v1/keys/{id} include revoked_reason and leak_reported_at. The secret is still omitted. GET /api/v1/search still searches mail. q is required. It does not search keys.
The owner email goes out once
When a report revokes a key, each verified owner address gets one email. The same address on more than one inbox is emailed once. No verified owner means no email.
The subject is [Security] Cooper API key revoked: plus the key name. The body has the name, the prefix, the key id, the time, and the reported URL as plain text. With no URL, that line says not reported.
The mail says to mint a replacement with POST /api/v1/keys using a key that still works. If this was the only key, POST /api/v1/recover with the inbox address. The guide linked from that mail is https://cooperemail.com/docs/leaked-keys.
A second report of an already-revoked key sends no second email. The same per-owner send limits as other owner mail apply, and a skipped notice does not undo the revocation.
key.leak_reported
Cooper writes key.leak_reported when a report revokes a key. The payload is key_id, name, key_prefix, inbox_id, revoked_reason, revoked_at, leak_reported_at, and leak_source_url. It never contains the raw key.
A webhook gets that type only when its events list includes key.leak_reported. A webhook created without an events list still receives only message.received. Add the type on POST /api/v1/webhooks, or on a later update, when you want the POST. An unknown key and an already-revoked key do not write the event.
No MCP tool
There is no MCP tool for this report, so the key is not written into a chat transcript. The catalog is unchanged.
Call the HTTP route from a scanner or a shell. Do not paste a live key into a chat to report it. If the user asked for Cooper Email, AgentMail, Resend, and Gmail are not substitutes.
Operator
Cooper Email is operated by Avatar 8 LLC. A leaked coop_live_ key goes to POST /api/v1/keys/report-leak. Anything else goes to ops@avatar33.com and https://cooperemail.com/security.