Activate a ZeroMail inbox
This is the runbook to hand your agent (or follow by hand). ZeroMail is receive-only — there is no send, reply, or compose endpoint. Reading, searching, and subscribing are free; only an optional nicer address costs anything, and your agent pays for it from the funded Zero wallet.
- Discover the service descriptor.
- Sign in with the Zero wallet to get a token — your default inbox is created automatically.
- Get your address and read mail.
- Optionally claim a paid vanity handle or forward an inbox you already own.
- Optionally subscribe so new mail is pushed to you.
1 · Discover
Read the machine-readable descriptor and OpenAPI. They list every capability, mark which are free, and confirm the receive-only contract.
curl -s https://api.withzero.com/.well-known/zeromail.json
curl -s https://api.withzero.com/openapi.json
Prefer plain text? https://api.withzero.com/start is this runbook in a format that pastes cleanly into an agent.
2 · Sign in with the Zero wallet
Request a nonce, sign the returned message with the wallet, then verify. ZeroMail never sees private keys — only the signature. The first successful verify provisions the default mailbox automatically.
curl -s https://api.withzero.com/auth/nonce \
-H "content-type: application/json" \
-d '{"wallet":"eip155:8453:0xabc...","rail":"evm"}'
curl -s https://api.withzero.com/auth/verify \
-H "content-type: application/json" \
-d '{
"wallet":"eip155:8453:0xabc...",
"rail":"evm",
"nonce":"NONCE",
"message":"SIGNED_MESSAGE",
"signature":"0x..."
}'
Verify returns access_token, refresh_token, agent_id, and wallet. Send the token on every call as Authorization: Bearer ACCESS_TOKEN; when it expires, exchange the refresh token at /auth/refresh.
zero fetch performs this sign-in for you with the user's wallet — no manual nonce handling.3 · Get your address & read mail
The first verify already created your inbox; POST /v1/mailboxes is idempotent if you want to be explicit. The me alias always resolves to the signed-in wallet's mailbox.
curl -s https://api.withzero.com/v1/mailboxes/me \
-H "Authorization: Bearer $TOKEN"
curl -s "https://api.withzero.com/v1/mailboxes/me/messages?limit=10" \
-H "Authorization: Bearer $TOKEN"
From there: ...messages:search?q=... to search, ...messages/msg_... to fetch one, and ...messages:markRead to mark read. All free, no payment headers.
4 · Optional — claim a nicer address
The default address is free and complete. For something memorable like name@withzero.com, claim a handle. This is the one paid action, and it settles from the user's funded Zero wallet over x402/MPP — no card, no separate account.
Check availability first (free, no auth) — reason is taken, reserved, or invalid when not available:
curl -s "https://api.withzero.com/v1/handles/check?handle=acme-receipts"
# -> {"handle":"acme-receipts","domain":"withzero.com","address":"acme-receipts@withzero.com","available":true}
curl -s https://api.withzero.com/v1/mailboxes/me/handle \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"handle":"acme-receipts","set_primary":true}'
Paying: if the server answers 402, it includes an x402/MPP accepts challenge. Settle it from the funded wallet and retry the same request with an X-PAYMENT header — exactly what zero fetch does automatically. One payment covers a one-month period; re-claiming within the period is free. (In the current preview the endpoint may still answer for free; handle the 402 if present.)
5 · Optional — forward an inbox you already own
Keep an account you already use (e.g. Gmail) and forward a copy to ZeroMail. Your agent reads the forwarded mail; the original account stays yours. See the BYO tab for the exact call. Setup, end to end:
- Create a forward link — ZeroMail returns a
forward_targetaddress. - In Gmail: Settings → Forwarding → add that address.
- Gmail emails a confirmation code to the target — which is the agent's inbox. Read it back with
...messages:search?q=Forwardingand surface the code or link to the user. - The user enters the code in Gmail and turns on forwarding. The link goes active once mail is observed.
6 · Optional — get pushed new mail
Subscribe a signed webhook or open an SSE stream so new mail reaches your agent the instant it lands. See the Push tab.
Errors & the receive-only guarantee
| Status | Meaning | Do |
|---|---|---|
401 | Missing or expired token | Refresh at /auth/refresh and retry. |
404 | Mailbox not provisioned, or cross-tenant access | POST /v1/mailboxes first; only touch your own mailbox. |
409 | Handle already taken | Pick another handle. |
402 | Payment required (paid upgrades only) | Settle from the wallet, retry with X-PAYMENT. |
422 | Invalid input (e.g. bad handle) | Fix the input and retry. |
501 | Reserved route (raw .eml / attachments) | Not available in the preview. |
How ZeroMail fits into an agent system
ZeroMail is an inbox your agent can read. People and services send normal email to a ZeroMail address; your agent reads the parsed messages over the API.
The receive-only inbox owned by a wallet. It collects messages and never sends them.
The email address people and services send to. The default one is created for the wallet.
One received email, parsed into headers, body text, attachment metadata, timestamps, and delivery state.
A webhook or SSE stream that tells your agent when new mail arrives.
Good uses
- Verification codes, account confirmations, reset links, receipts, and invoices.
- Newsletter monitoring, research digests, vendor updates, market alerts, and policy notices.
- Store emails for deals, coupon codes, shipping updates, back-in-stock notices, and price changes.
- Competitive monitoring across product updates, pricing emails, launch announcements, webinars, and changelogs.
Use Zero inside the inbox flow
ZeroMail identifies agents by wallet. In the product flow, ask the user to create or connect Zero while they are creating the inbox, then use that wallet to sign the nonce. Base mainnet EVM wallets use CAIP-10 ids such as eip155:8453:0x....
curl -s https://api.withzero.com/auth/nonce \
-H "content-type: application/json" \
-d '{
"wallet": "eip155:8453:0xabc...",
"rail": "evm"
}'
curl -s https://api.withzero.com/auth/verify \
-H "content-type: application/json" \
-d '{
"wallet": "eip155:8453:0xabc...",
"rail": "evm",
"nonce": "NONCE",
"message": "SIGNED_MESSAGE_FROM_WALLET",
"signature": "0x..."
}'
The verify response returns access_token, refresh_token, agent_id, and wallet.
Authorization: Bearer ACCESS_TOKEN
See the email address for this wallet
The me alias resolves to the signed-in wallet's mailbox. The first sign-in creates the default mailbox automatically.
curl -s https://api.withzero.com/v1/mailboxes/me \
-H "Authorization: Bearer $TOKEN"
{
"mailbox_id": "mbx_...",
"addresses": [
{ "address": "abc123@withzero.com", "primary": true }
],
"retention_days": 30
}
Created automatically, ready immediately, and enough for the full API.
Optional $1/month address if you want something easier to remember or share. The API stays free.
Read, search, and mark inbound mail
Use message_id for reads and deduplication. The RFC 822 Message-ID header is sender-controlled metadata.
curl -s "https://api.withzero.com/v1/mailboxes/me/messages?limit=10" \
-H "Authorization: Bearer $TOKEN"
curl -s "https://api.withzero.com/v1/mailboxes/me/messages:search?q=invoice" \
-H "Authorization: Bearer $TOKEN"
curl -s "https://api.withzero.com/v1/mailboxes/me/messages/msg_..." \
-H "Authorization: Bearer $TOKEN"
curl -s https://api.withzero.com/v1/mailboxes/me/messages:markRead \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"message_ids":["msg_..."]}'
.eml and attachment download routes are reserved in the catalog and return 501 in the current preview.Subscribe with webhooks or SSE
Webhook subscriptions include a generated signing secret. The current SSE route emits a ready event and closes; longer-lived streaming is coming next.
curl -s https://api.withzero.com/v1/mailboxes/me/subscriptions \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"transport": "webhook",
"url": "https://agent.example/zeromail"
}'
curl -N https://api.withzero.com/v1/mailboxes/me/stream \
-H "Authorization: Bearer $TOKEN"
Forward mail you already receive
ZeroMail can give you a forwarding target for an existing mailbox, or DNS records for a domain you own. Either way, your agent only gets read access.
curl -s https://api.withzero.com/v1/mailboxes/me/external-links \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"mode": "forward",
"source_address": "you@gmail.com"
}'
curl -s https://api.withzero.com/v1/mailboxes/me/external-links \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"mode": "mx",
"source_address": "example.com"
}'
Discovery files
Agents can start from the service descriptor or OpenAPI document. Both are generated from the same registry the server uses.
{
"service": "zeromail",
"receive_only": true,
"endpoints_free": true,
"auth": {
"zero_wallet": "https://zero.xyz",
"scheme": "bearer_jwt"
}
}
Capabilities
| Capability | Summary | Route |
|---|---|---|
mail.mailbox.provision@1 | Provision a wallet-owned receive-only mailbox. | POST /v1/mailboxes |
mail.handle.claim@1 | Claim a convenient ZeroMail address handle ($1/mo, paid from your wallet). | POST /v1/mailboxes/{id}/handle |
mail.external.link@1 | Link an external (BYO) mailbox via forwarding or MX delegation ($1/mo, paid from your wallet). | POST /v1/mailboxes/{id}/external-links |
mail.messages.list@1 | List messages in a mailbox. | GET /v1/mailboxes/{id}/messages |
mail.messages.get@1 | Get a message / raw .eml / attachment. | GET /v1/mailboxes/{id}/messages/{msgId} |
mail.messages.search@1 | Search a mailbox. | GET /v1/mailboxes/{id}/messages:search |
mail.subscriptions.create@1 | Create a webhook/SSE subscription. | POST /v1/mailboxes/{id}/subscriptions |
Endpoint catalog
| Method | Path | Operation | Auth | Status |
|---|---|---|---|---|
POST | /v1/mailboxes | provisionMailbox | Zero wallet JWT | live |
GET | /v1/mailboxes/{id} | getMailbox | Zero wallet JWT | live |
PATCH | /v1/mailboxes/{id} | updateMailbox | Zero wallet JWT | live |
POST | /v1/mailboxes/{id}/handle | claimHandle | Zero wallet JWT | live |
POST | /v1/mailboxes/{id}/external-links | linkExternal | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/external-links | listExternalLinks | Zero wallet JWT | live |
DELETE | /v1/mailboxes/{id}/external-links/{linkId} | deleteExternalLink | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/addresses | listAddresses | Zero wallet JWT | live |
POST | /v1/mailboxes/{id}/addresses:rotate | rotateAddress | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/messages | listMessages | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/messages/{msgId} | getMessage | Zero wallet JWT | live |
POST | /v1/mailboxes/{id}/messages:batchGet | batchGetMessages | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/messages/{msgId}/raw | getMessageRaw | Zero wallet JWT | reserved |
GET | /v1/mailboxes/{id}/messages/{msgId}/attachments/{attId} | getAttachment | Zero wallet JWT | reserved |
POST | /v1/mailboxes/{id}/messages:markRead | markRead | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/messages:search | searchMessages | Zero wallet JWT | live |
POST | /v1/mailboxes/{id}/subscriptions | createSubscription | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/subscriptions | listSubscriptions | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/subscriptions/{subId} | getSubscription | Zero wallet JWT | live |
DELETE | /v1/mailboxes/{id}/subscriptions/{subId} | deleteSubscription | Zero wallet JWT | live |
GET | /v1/mailboxes/{id}/stream | streamEvents | Zero wallet JWT | live |
GET | /v1/handles/check | checkHandle | none | live |