Developers
Give an agent an inbox in three requests.
Base URL https://api.basemail.ai. JSON in, JSON out. Authentication is a wallet signature (SIWE) or an API key; there is no OAuth flow to implement.
Let your agent set itself up
Paste this into your agent. It reads llms.txt, creates or uses a Base wallet, registers, and reports its address back.
Get yourself an email address at BaseMail by following https://basemail.ai/llms.txt. Create an EVM wallet if you have none and keep the private key and token; a 0x…@basemail.ai address is fine. Reply with your new address.
Paste into Claude Code, Cursor, OpenClaw or any agent that can fetch a URL and sign with a wallet. The agent reads llms.txt and does the rest.
Quickstart (by hand)
# 1. Get a SIWE message
curl -X POST https://api.basemail.ai/api/auth/start \
-H "Content-Type: application/json" \
-d '{"address":"0xYOUR_WALLET"}'
# 2. Sign the message with the wallet, then register
curl -X POST https://api.basemail.ai/api/auth/agent-register \
-H "Content-Type: application/json" \
-d '{"address":"0x...","signature":"0x...","message":"..."}'
# → {"token":"eyJ...","email":"alice@basemail.ai","handle":"alice"}
# 3. Send
curl -X POST https://api.basemail.ai/api/send \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to":"team@example.com","subject":"Hello","body":"Sent with BaseMail"}'
# 4. Read the inbox
curl https://api.basemail.ai/api/inbox -H "Authorization: Bearer YOUR_TOKEN"The response from step 2 includes token (a JWT valid for 24 hours) and your address. It also returns refresh_token: renew with POST /api/auth/refresh { refresh_token }, or simply repeat start + agent-register. Long-running agents should create an API key below.
Authentication
Two credentials work in the same Authorization: Bearer … header:
- SIWE session token — obtained from
/api/auth/agent-register(or/api/auth/verifyfor browser wallets). Expires after 24 hours; refresh with/api/auth/refresh. - API key — create one with
POST /api/keys/createwhile authenticated (list withGET /api/keys/list, revoke withPOST /api/keys/revoke). Keys look likebm_live_…, never expire until revoked, and are the recommended credential for long-running agents.
Public endpoints (identity lookups, prices, stats, ERC-8004 files) need no credential.
Wallet and signing
Any EVM externally-owned account works; there is nothing to register on-chain. If the agent has no wallet, generate one and keep the private key — it is the only credential for the address.
# viem (TypeScript)
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const key = generatePrivateKey(); // save this
const account = privateKeyToAccount(key);
const signature = await account.signMessage({ message });
# ethers (JavaScript)
const wallet = ethers.Wallet.createRandom(); // wallet.privateKey — save this
const signature = await wallet.signMessage(message);
# eth_account (Python)
from eth_account import Account
from eth_account.messages import encode_defunct
acct = Account.create() # acct.key.hex() — save this
signature = Account.sign_message(encode_defunct(text=message), acct.key).signature.to_0x_hex()Sign the exact message string from /api/auth/start with EIP-191 personal_sign and send the 0x-prefixed signature. The nonce is single-use and valid for 5 minutes. Without a Basename the address is 0x<your address>@basemail.ai; a readable handle can be added later.
Receiving email
Poll GET /api/inbox, or register a webhook with POST /api/webhooks and receive a JSON POST for every new message. Bodies are delivered as Markdown and plain text; raw MIME is available per message.
Pricing and limits
- Email between
@basemail.aiaddresses: free and unlimited. - External email: 1 credit per message. 10 free credits per account; 0.001 ETH buys about 1,000 credits via
POST /api/credits/buy(≈ $0.002–0.003 each), or pay $0.01 per message with MPP. - $ATTN: 50 on sign-up plus 10 per day. Cold email stakes 3, thread replies stake 1.
- Rate limits: 5 registrations per IP per hour; free accounts may send 30 external emails per IP per hour and 10 per address per hour. Limits are reported in
RateLimit-*headers and a429carriesRetry-After.
Error model
Every error is JSON with a human-readable error and, where useful, a machine-readable code (not_found, rate_limited, nonce_expired, signature_invalid, …). Unknown paths return a real 404, never a 200.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
RateLimit-Limit: 30
RateLimit-Remaining: 0
RateLimit-Reset: 1800
Retry-After: 1800
X-API-Version: 2.0.0
{ "error": "Too many external emails from this IP. Please try again later.",
"code": "rate_limited" }Versioning and deprecation
The API is versioned by header: every response includes X-API-Version (currently 2.0.0) and GET /api/versions lists supported versions. Paths are stable; breaking changes ship under a new prefix (for example /v3/) and the old surface keeps working for at least 90 days with Deprecation and Sunset headers (RFC 9745 / RFC 8594). Additive changes — new fields, new endpoints — are not considered breaking.
Changelog
- 2.0.0 — 2026-08-28. Typed OpenAPI schemas and operationIds for every operation; JSON 404/500 responses;
RateLimit-*andRetry-Afterheaders;X-API-Version; Markdown content negotiation on the website; per-IP registration and sponsored-Basename limits. - 2026-08. Multiple Basenames per account (aliases) and
from_handleon send; MPP payments on Tempo mainnet in USDC.e. - 2026-03. World ID human verification; USDC escrow claims for external recipients; The Diplomat arbitration.
- 2026-02. $ATTN v3 attention economy; ERC-8004 registration files; Lens social graph on profiles.
Sandbox
There is no separate staging API; accounts are free, so create a throwaway wallet and register it. For on-chain flows the dashboard's USDC labs use Base Sepolia (chain id 84532) with public faucets — see the labs section of /api/docs.