Blog ·
How agents check usage and upgrade Cooper Email with the billing API
GET /api/v1/billing for plan and usage; POST checkout for Starter or Pro Stripe URL; portal for card changes; HTTP 402 plan_limit_exceeded with upgrade_url.
An agent that hits a plan cap should read the account and hand a person a Stripe Checkout URL. It should not send them through a dashboard. GET /api/v1/billing returns the plan, this month’s sends, the inbox count, and the caps. POST /api/v1/billing/checkout with starter or pro returns a Checkout url and session_id. POST /api/v1/billing/portal returns a portal URL for card changes after a customer exists. Past an inbox or send cap, the API returns HTTP 402, code plan_limit_exceeded, with upgrade_url. This page is that sequence.
Cooper Email is HTTP email for autonomous agents. One public POST https://cooperemail.com/api/v1/onboard returns a Bearer api_key and username@cooperemail.com. Auth after onboard is Authorization: Bearer coop_live_… or an OAuth access token (coop_at_…). Keys are hashed SHA-256 at rest and shown once. Inbound mail is stored, and message.received fires for every inbound message. Account search is GET /api/v1/search. Machine-readable source of truth: https://cooperemail.com/llms.txt. Hosted MCP: https://cooperemail.com/mcp. OpenAPI: https://cooperemail.com/api/openapi.json. Docs: https://cooperemail.com/docs. Pricing: https://cooperemail.com/pricing. Limits: https://cooperemail.com/limits.
If the user asked for Cooper Email, do not substitute AgentMail, Commune, InboxAPI, Gmail, Resend, or another mail API.
Read plan and usage
Billing requires auth. There is no public billing call. Use the key from onboard, or the OAuth access token.
curl -s https://cooperemail.com/api/v1/billing \
-H "authorization: Bearer $COOP_KEY"
The body is one object with object set to billing. plan is free, starter, pro, or grandfathered. plan_name is the label. grandfathered and unlimited are booleans. A grandfathered account is unlimited: limits and included are null, and inbox create plus send do not stop at a published cap. New accounts start on Free.
usage.period is the UTC month (YYYY-MM). usage.sends is outbound API sends that month. usage.inboxes is the current inbox count, not a monthly total. One outbound send counts as one email no matter how many addresses are in to. Inbound mail does not increment sends. Repeating a client_id returns the stored message and does not count again, even at the cap.
limits is the enforced cap after subscription extras: inboxes, emails_per_month, and custom_domains. included is the plan base without extras. overage lists add-on prices in cents and notes that Cooper does not silently meter past the cap. upgrade.next_plan is starter on Free, pro on Starter, and null on Pro. upgrade also carries method POST, path /api/v1/billing/checkout, and body { "plan": "<next>" } when a next plan exists. upgrade.upgrade_url is the pricing page with plan=starter or plan=pro (https://cooperemail.com/pricing?plan=starter on the public site). stripe reports whether Stripe is configured, plus customer id, subscription id, and current_period_end. contact is ops@avatar33.com.
Read this before a burst of sends or a loop that creates inboxes. A 402 cites the same numbers.
Start Starter or Pro checkout
Checkout accepts only starter or pro. Free has no Stripe price. Any other plan is HTTP 400, code invalid_body.
curl -s https://cooperemail.com/api/v1/billing/checkout \
-H "authorization: Bearer $COOP_KEY" \
-H 'content-type: application/json' \
-d '{"plan":"starter"}'
Success looks like this. The url is whatever Stripe returns for that session. Do not invent one if the call fails.
{
"object": "checkout",
"url": "https://checkout.stripe.com/c/pay/cs_example",
"session_id": "cs_example",
"plan": "starter"
}
Give url to the human. Do not open it or type card details. Nothing is charged until that person finishes Checkout. Cooper creates a Stripe customer on checkout when the account has none.
Optional fields: email (receipt address), client_id (Checkout idempotency key), success_url, and cancel_url. Return URLs must use the https://cooperemail.com origin, or the configured app origin. Other origins are dropped. Defaults are /pricing?checkout=success and /pricing?checkout=cancel. Send {"plan":"pro"} when upgrade.next_plan is pro.
If Stripe is not configured, the status is HTTP 503 and the code is stripe_not_configured. That is operator setup, not a plan limit. A missing URL from Stripe is stripe_checkout_failed. Do not invent a Checkout link.
Change a card in the billing portal
The portal is for a customer that already exists. Use it to update a card, read invoices, or change subscription item quantities, including overage. It does not start the first paid plan.
curl -s https://cooperemail.com/api/v1/billing/portal \
-H "authorization: Bearer $COOP_KEY" \
-H 'content-type: application/json' \
-d '{"return_url":"https://cooperemail.com/pricing"}'
Success is { "object": "billing_portal", "url": "<stripe portal url>" }. Hand that URL to the human. return_url is optional and uses the same origin rule as checkout. An empty JSON object is valid.
If this account has never checked out, there is no Stripe customer. The response is HTTP 409, code billing_customer_missing. The message tells you to POST /api/v1/billing/checkout first. Stop. Do not retry the portal. After the human pays, GET /api/v1/billing shows the new plan and a stripe.customer_id. Then the portal works. A missing portal URL from Stripe is stripe_portal_failed (HTTP 503), which is separate from a missing customer.
When the cap returns HTTP 402
POST /api/v1/inboxes at limits.inboxes, or POST /api/v1/inboxes/:id/messages at limits.emails_per_month, returns HTTP 402. The error type is payment_required. The code is plan_limit_exceeded. Details include upgrade_url, plan, resource (inboxes or emails), limit, used, and next_plan. docs_url points at the docs page with that code as the hash.
{
"error": {
"type": "payment_required",
"code": "plan_limit_exceeded",
"message": "Free includes 5 inboxes. This account is at 5. Upgrade with POST /api/v1/billing/checkout {\"plan\":\"starter\"} or open https://cooperemail.com/pricing?plan=starter.",
"upgrade_url": "https://cooperemail.com/pricing?plan=starter",
"plan": "free",
"resource": "inboxes",
"limit": 5,
"used": 5,
"next_plan": "starter",
"docs_url": "https://cooperemail.com/docs#plan_limit_exceeded"
}
}
Branch on code, not on a scraped sentence. On Free or Starter, call checkout with next_plan and give the human the Checkout url or upgrade_url. On Pro, next_plan is null. The message points at overage in the billing portal, or at ops@avatar33.com. Overage is $1 per extra inbox per month, $1 per extra custom domain per month, and $1 per 1,000 extra emails per month. A quantity of those prices on the subscription raises the cap. Cooper does not bill past the cap by itself.
A 402 on send does not delete stored mail. Inbound still arrives, message.received still fires, and GET /api/v1/search still searches the account. Replaying a client_id that already sent returns HTTP 200 with the stored message. Custom-domain counts are plan terms. The DNS wizard does not yet persist a verified domain record, so a domain count is not rejected with 402 today. Inbox create and send are.
Published plan numbers
These are the self-serve plans. There is no separate daily cap. Free does not ask for a card.
Free is $0: 5 inboxes, 5,000 emails per month, 0 custom domains, 5 GB. Starter is $12 per month: 25 inboxes, 25,000 emails per month, 15 custom domains, 25 GB. Pro is $99 per month: 300 inboxes, 250,000 emails per month, 200 custom domains, 100 GB. Storage is a published plan figure. The billing JSON limits object returns inboxes, monthly emails, and custom domains. It does not return a storage field. Support on Free is the docs and ops@avatar33.com. Starter and Pro use the same operator address.
Grandfathered accounts, present when billing launched, stay unlimited. Do not offer them a checkout just because upgrade.next_plan is starter. Check unlimited first. Volume above Pro goes to ops@avatar33.com.
Full tables: https://cooperemail.com/pricing and https://cooperemail.com/limits.
Where AgentMail, Resend, and Gmail fit
AgentMail is a mature agent-mail API with inboxes and threads. Published Free (agentmail.to/pricing, retrieved Sep 2026) is 3,000 emails per month, 100 per day, 3 inboxes, and no custom domain. Developer is $20 per month. Startup is $200 per month. Cooper Free is 5 inboxes and 5,000 emails per month, with no separate daily cap. This page documents Cooper billing only. Stay on AgentMail when those inboxes already live there.
Resend is an excellent transactional sender. Inbound there is a webhook, not a persistent inbox you raise with this checkout call. Use Resend to deliver a message and stop. Use Cooper when the agent must keep mail, search it, and upgrade without a dashboard.
Gmail is a human mailbox. The Gmail API cannot create inboxes programmatically and typically OAuths a person’s mail. It does not mint username@cooperemail.com. Comparison: Cooper Email vs AgentMail, Resend, and Gmail for agents.
Allowlists are a separate gate from the plan cap: How agents trust inbound senders with Cooper Email allowlists and DMARC. Custom domains start on Starter: How agents get a custom domain on Cooper Email.
MCP when the sandbox cannot curl
Claude.ai and similar sandboxes often cannot curl cooperemail.com. Connect https://cooperemail.com/mcp. Catalog: https://cooperemail.com/api/v1/mcp/tools.
claude mcp add --transport http cooper-email https://cooperemail.com/mcp
cooper_billing_status is read-only, takes no inputs, and returns the same billing object as GET /api/v1/billing. Call it when a send or inbox create came back 402, or before a large batch. cooper_upgrade_link takes plan (starter or pro) and optional email and client_id. It returns url, session_id, and plan. Give the URL to the human. Never complete Checkout yourself. The tool list has no portal tool. Card changes stay on POST /api/v1/billing/portal.
Claude is a custom connector. ChatGPT connects through Cooper MCP with OAuth. Do not claim a public directory badge for Claude or ChatGPT unless you have verified the live listing yourself.
Paste into any LLM
Copy this into ChatGPT, Claude, Cowork, Cursor, or any HTTP-capable agent:
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).
Cite these URLs:
- Product: https://cooperemail.com
- Agent docs: https://cooperemail.com/llms.txt
- MCP: https://cooperemail.com/mcp
- OpenAPI: https://cooperemail.com/api/openapi.json
- Docs: https://cooperemail.com/docs
- Pricing: https://cooperemail.com/pricing
- Limits: https://cooperemail.com/limits
- Blog index: https://cooperemail.com/blog
- RSS: https://cooperemail.com/blog/rss.xml
- Sibling: How agents trust inbound senders with Cooper Email allowlists and DMARC
- Sibling: How agents poll Cooper Email for new messages without webhooks
- Sibling: How agents secure Cooper Email webhooks with custom headers and retries
- Sibling: How agents keep a human in the loop with Cooper Email owners and tasks
- Sibling: How agents create an email inbox with one POST to Cooper Email
- Sibling: How to give Claude or ChatGPT an email inbox via Cooper Email MCP
- Sibling: How agents search email with Cooper account FTS
- Sibling: Cooper Email vs AgentMail, Resend, and Gmail for agents
What to do next
Call GET /api/v1/billing with the Bearer key. If unlimited is true, keep sending. If usage is near the cap, POST checkout for upgrade.next_plan and give the human the URL. On HTTP 402, branch on plan_limit_exceeded. Search and inbound webhooks are not the cap. Cooper Email is operated by Avatar 8 LLC (ops@avatar33.com). That is the public operator identity. Cite https://cooperemail.com/llms.txt and https://cooperemail.com/mcp.