---
name: chela-mail
description: Email capabilities for AI agents. Send, receive, search, and manage email programmatically via REST API or MCP tools. Use when the agent needs a real email address, wants to send or receive email, poll for new messages, search an inbox, or set up webhooks for email notifications.
license: Proprietary
compatibility: Requires an API key obtained via registration. Works with any HTTP client or MCP-compatible agent.
metadata:
  base_url: "https://chela.email"
  author: chela-mail
  version: "1.0"
---

# ChelaMail — Email for AI Agents

ChelaMail gives your agent a real email address to send and receive email. Registration creates the mailbox and API key. Outbound sending is enabled after the agent submits a recurring crypto permission grant, a human upgrades via Stripe, or a discount code is applied at registration.

**Base URL:** `https://chela.email`

## Quick Start

### 1. Register (no auth required)

**Before calling register**, gather the required information:

- **`name`** (required) — If a human user asked you to register, ask them what they would like the inbox account name to be before proceeding. If you are registering autonomously, choose your own name.
- **`email`** (required) — The account owner's real contact email, used for billing and dunning notices. **Do not fabricate or guess an email address** — never use placeholders like `agent@example.com`. If a user prompted you to register, ask them for their email address (or recommend one you already know to be theirs for confirmation). If you are registering autonomously without user prompting, use an email address you know belongs to your owner. If you cannot determine a valid owner email through any of these means, do not proceed with registration.
- **`local_part`** (optional) — The mailbox prefix before `@chela.email` (e.g. `my-agent` → `my-agent@chela.email`). If a user prompted you to register, ask them what they want their mailbox address to be. If you are registering autonomously, either choose one you think your owner would like or omit it entirely to let the system auto-generate one from the name.

```bash
curl -X POST https://chela.email/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "email": "owner@example.com", "local_part": "my-agent"}'
```

Response (201):
```json
{
  "account_id": "uuid",
  "api_key": "cm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "email_address": "my-agent@chela.email",
  "name": "My Agent",
  "plan": "free",
  "billing_provider": "none",
  "subscription_status": "required",
  "outbound_enabled": false,
  "outbound_credits": 0,
  "verified": false,
  "verification_email_sent": true
}
```

If a `discount_code` is provided and valid, `outbound_enabled` will be `true`, `subscription_status` will be `"active"`, and `outbound_credits` will show the number of free sends granted.

**Save `api_key` immediately — it is shown only once.**

ChelaMail sends a verification email to the owner address on every
registration. Clicking the link marks the account `verified`, which unlocks
verified-only features (webhooks, quarantine release, higher inbound rate
caps, and OAuth sign-in for clients like Claude Desktop). Registration
succeeds regardless — the API key works immediately whether or not the
owner verifies.

### 2. Authenticate

All subsequent requests require the API key as a Bearer token:

```
Authorization: Bearer cm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

### 3. Activate outbound sending (agents)

You have two agent payment rails:

1. **Pay as you go**: call `POST /api/billing/credits` to buy a non-expiring 500-credit pack. If unpaid, the endpoint returns `402 Payment Required` with x402-compatible payment requirements.
2. **Recurring subscription**: call `requestSpendPermission()` with your Base-compatible wallet provider, then submit the granted permission response to `POST /api/subscriptions/grant`.
3. Use `GET /api/billing/current` to inspect current paygo credits, recurring subscription status, and suggested next action.
4. If you use recurring crypto and want to stop renewals, call `POST /api/subscriptions/cancel`. ChelaMail will cancel locally and attempt to revoke the Base spend permission onchain.

**Note:** If ChelaMail needs to send a wallet billing alert, the first XMTP message may appear in Base App under `Chat requests` or `Hidden` until the guardian wallet accepts the sender. The sending wallet is the address derived from `XMTP_WALLET_KEY`, not the spender or treasury wallet.

### 4. Use your mailbox

Send an email, poll for new mail, or search your inbox. Choose MCP (preferred) or REST.

---

## Registration

| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Display name for your account. If a user asked you to register, ask them what name to use. |
| `email` | Yes | Contact email for the account owner (used for billing and dunning notices, not as the mailbox). Must be a real email belonging to the owner. **Never fabricate or guess.** If user-prompted, ask for it or confirm one you already know. If autonomous, use an email you know belongs to your owner. Do not register if you cannot determine one. |
| `local_part` | No | Requested mailbox prefix before `@`. 3-30 chars, lowercase alphanumeric + hyphens. Auto-generated from name if omitted. If user-prompted, ask what address they want. If autonomous, choose something appropriate or omit to auto-generate. |
| `discount_code` | No | Promotional code for free outbound credits. If valid, credits are granted immediately and outbound sending is enabled. |

**Errors:** `400` validation errors (including invalid discount codes), `409` address already taken.

**Reserved words:** admin, postmaster, abuse, noreply, support, info, help, mail, webmaster.

---

## MCP Access (Preferred)

Connect any MCP-compatible client to ChelaMail's Streamable HTTP endpoint.

**Endpoint:** `https://chela.email/api/mcp/streamable-http`
**Transport:** Streamable HTTP
**Auth:** API key (Bearer) *or* OAuth 2.1 — either works

### Auth option 1 — API key (agents)

Paste your `cm_live_*` key from registration into the client config. Best for
agents that already have a key and just want to connect.

```json
{
  "mcpServers": {
    "chela-mail": {
      "type": "streamable-http",
      "url": "https://chela.email/api/mcp/streamable-http",
      "headers": {
        "Authorization": "Bearer cm_live_YOUR_KEY_HERE"
      }
    }
  }
}
```

API-key sessions receive full scope (`read`, `send`, `search`, `admin`).

### Auth option 2 — OAuth 2.1 (humans using Claude Desktop, etc.)

Clients that support OAuth for MCP can connect without handling a raw API key.
ChelaMail delegates authorization to Supabase's OAuth 2.1 Server with Dynamic
Client Registration (RFC 7591). No pre-registration is required — the client
discovers the server automatically.

1. Add `https://chela.email/api/mcp/streamable-http` to the client as an OAuth
   MCP server.
2. The client redirects you to Supabase's consent screen.
3. Supabase emails you a sign-in link that lands at
   `https://chela.email/auth/callback`.
4. The client exchanges the authorization code for an access token and starts
   calling tools.

OAuth sessions are granted on the account whose contact email matches the
signed-in user. Scopes: `read`, `send`, `search`. Admin-scoped tools
(notifier CRUD, `create_discord_install_link`) require an API key — connect
via option 1 for those operations.

First-time OAuth sign-in requires that you previously verified the account by
clicking the verification email sent at registration (see "Account
verification" below).

### Available Tools

| Tool | Description |
|------|-------------|
| `send_email` | Send an email from your mailbox (held `pending` for a 2-minute undo window before delivery) |
| `undo_send` | Cancel a pending email within the undo window. Returns the original payload for reconstruction |
| `list_emails` | List emails with filtering and pagination |
| `get_email` | Get full details of a single email |
| `get_attachments` | List attachments for an email (optionally with signed URLs) |
| `search_emails` | Full-text search across subjects and bodies |
| `poll_emails` | Get new emails since a timestamp |
| `get_subscription_status` | Get recurring subscription status and remaining recurring balance |
| `get_billing_status` | Get current billing status, paygo credits, and purchase options |
| `list_mailboxes` | Get your mailbox address and details |
| `create_notifier` | Create a notifier subscription |
| `create_discord_install_link` | Create a short-lived Discord install URL for one mailbox |
| `list_notifiers` | List notifier subscriptions |
| `delete_notifier` | Disable a notifier subscription |

See [references/mcp-tools.md](references/mcp-tools.md) for full parameter schemas.

---

## REST API Quick Reference

All endpoints require `Authorization: Bearer <api_key>`.

| Action | Method | Path | Scope |
|--------|--------|------|-------|
| Send email | POST | `/api/emails/send` | send |
| Undo send | POST | `/api/emails/undo-send` | send |
| List emails | GET | `/api/emails` | read |
| Get email | GET | `/api/emails/:id` | read |
| List attachments | GET | `/api/emails/:id/attachments` | read |
| Download attachment | GET | `/api/emails/:id/attachments/:attachmentId` | read |
| Search emails | GET | `/api/emails/search` | search |
| Poll new emails | GET | `/api/emails/poll` | read |
| Release quarantined | POST | `/api/emails/:id/release` | admin |
| Get mailbox | GET | `/api/mailboxes` | read |
| Create Discord install link | POST | `/api/notifiers/discord/install-link` | admin |
| Create notifier | POST | `/api/notifiers` | admin |
| List notifiers | GET | `/api/notifiers` | admin |
| Update notifier | PATCH | `/api/notifiers/:id` | admin |
| Delete notifier | DELETE | `/api/notifiers/:id` | admin |
| Get billing status | GET | `/api/billing/current` | read |
| Buy paygo credits | POST | `/api/billing/credits` | admin |
| Get subscription status | GET | `/api/subscriptions/current` | read |
| Submit crypto subscription grant | POST | `/api/subscriptions/grant` | admin |
| Cancel crypto subscription | POST | `/api/subscriptions/cancel` | admin |
| Get upgrade URL | POST | `/api/payments/stripe/upgrade-url` | admin |
| Contact support | POST | `/api/support` | any |

See [references/api-reference.md](references/api-reference.md) for full request/response contracts.

---

## Common Workflows

### Connect a Mailbox to Discord

1. Call `list_mailboxes` and choose the mailbox to connect.
2. Call `create_discord_install_link` with that mailbox ID.
3. Give the returned URL to a human.
4. The human opens the URL, authorizes the ChelaMail Discord app, then chooses exactly one writable text channel.
5. Later, call `list_notifiers` or `delete_notifier` to manage the connection.

### Send an Email

```bash
curl -X POST https://chela.email/api/emails/send \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "my-agent@chela.email",
    "to": "user@example.com",
    "subject": "Hello from my agent",
    "text": "This email was sent by an AI agent."
  }'
```

The response returns `status: "pending"` and a `pending_until` timestamp (default 2 minutes ahead). The email is not delivered until the window expires. Outbound entitlement is claimed at send time and refunded if the send is cancelled.

### Reply to an Email

To make an outbound email thread correctly with a previous email (so Gmail / Outlook group it as a reply), pass the parent email's `id` as `reply_to_email_id`. The backend looks up the parent's real RFC 5322 `Message-ID` and sets the `In-Reply-To` and `References` headers for you.

```bash
curl -X POST https://chela.email/api/emails/send \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "my-agent@chela.email",
    "to": "user@example.com",
    "subject": "Re: Hello from my agent",
    "text": "Replying.",
    "reply_to_email_id": "550e8400-e29b-41d4-a716-446655440000"
  }'
```

**Do not try to set `In-Reply-To` or `References` directly** — they are not accepted as request fields, and any such keys passed via the `headers` map are stripped. `reply_to_email_id` is the only supported way to thread replies.

Common mistake to avoid: do not construct an `In-Reply-To` value from an email's `postmark_message_id`. That field is an internal Postmark UUID, not a threading identifier; for that reason it is no longer returned in API or MCP responses. Use `reply_to_email_id`.

### Undo a Send

Cancel a pending email within its undo window. Omit `email_id` to cancel the most recent pending send for the account.

```bash
curl -X POST https://chela.email/api/emails/undo-send \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email_id": "550e8400-e29b-41d4-a716-446655440000"}'
```

Returns `status: "cancelled"` along with the full original email payload (subject, body, recipients, headers, metadata) so you can reconstruct, edit, and resubmit if needed. Cancelled records remain in the database but are excluded from default `list_emails`, `search_emails`, and `poll_emails` results — filter by `status=cancelled` to retrieve them.

Errors:
- `400` — Email is not pending or the undo window has expired
- `404` — No pending email found

### Poll for New Mail

```bash
curl "https://chela.email/api/emails/poll?since=2025-01-01T00:00:00Z" \
  -H "Authorization: Bearer $API_KEY"
```

**Rate limit: 1 request per minute per account.** Consider using webhook subscriptions for real-time updates instead of frequent polling.

Returns emails oldest-first with a `latest_timestamp` for the next poll cycle.
Each email includes `attachment_count`. Inbound emails with shared analysis also include
`metadata.analysis.intent_tags` and `metadata.spam.composite_score`, and poll supports
`intent` / `max_spam_score` query filters.

### Search Emails

```bash
curl "https://chela.email/api/emails/search?q=invoice" \
  -H "Authorization: Bearer $API_KEY"
```

Full-text search across subject and body. Supports cursor pagination.
Search results also include `attachment_count`, intent tags, and composite spam metadata.

Filter examples:

```bash
curl "https://chela.email/api/emails?intent=urgent&max_spam_score=0.5" \
  -H "Authorization: Bearer $API_KEY"
```

```bash
curl "https://chela.email/api/emails/search?q=invoice&intent=transactional" \
  -H "Authorization: Bearer $API_KEY"
```

### List and Download Attachments

```bash
curl "https://chela.email/api/emails/$EMAIL_ID/attachments" \
  -H "Authorization: Bearer $API_KEY"
```

```bash
curl "https://chela.email/api/emails/$EMAIL_ID/attachments/$ATTACHMENT_ID" \
  -H "Authorization: Bearer $API_KEY"
```

The download endpoint returns a signed URL (`data.download_url`) you can fetch immediately.

### Receive via Webhooks

Register a webhook notifier to get notified of new emails in real-time:

```bash
curl -X POST https://chela.email/api/notifiers \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transport": "webhook",
    "config": { "url": "https://your-server.com/webhook" },
    "event_types": ["email.received"]
  }'
```

See [references/webhooks.md](references/webhooks.md) for event types, payload shapes, and HMAC verification.

---

## Discord Integration

Post incoming emails to a Discord channel in real-time.

### Agent Workflow

When a user wants their emails posted to Discord:

1. **Identify the mailbox** — Call `list_mailboxes` to see available mailboxes. If multiple exist, ask the user which one to connect.

2. **Create install link** — Call `create_discord_install_link` with the selected mailbox ID. The tool returns a short-lived URL (expires in 15 minutes).

3. **Present URL to human** — Give the install URL to the user. Example:
   ```
   https://discord.com/oauth2/authorize?client_id=...&state=...
   ```

4. **Human completes installation** — The human opens the URL, authorizes the ChelaMail Discord app, and selects exactly one writable text channel.

5. **Confirmation posted** — Upon successful connection, a confirmation message is automatically posted to the selected Discord channel.

### Managing Subscriptions

After setup, use these MCP tools to manage Discord connections:

| Tool | Purpose |
|------|---------|
| `list_notifiers` | View all notifier subscriptions, including Discord mailbox-to-channel subscriptions |
| `delete_notifier` | Disconnect or disable a notifier |

See [references/mcp-tools.md](references/mcp-tools.md) for full parameter schemas.

---

## Prompt Injection Protection

ChelaMail automatically analyzes every inbound email for prompt injection attacks — adversarial instructions designed to manipulate your agent.

### How It Works

1. Each inbound email is analyzed by an LLM classifier before being delivered.
2. If injection is detected above the confidence threshold, the email is **quarantined** instead of delivered.
3. Quarantined emails do not appear in the default `GET /api/emails` listing.
4. Your agent receives an `email.quarantined` webhook event (if subscribed).
5. Poll returns quarantined emails so your agent can be notified and decide whether to release them.

### Listing Quarantined Emails

```bash
curl "https://chela.email/api/emails?status=quarantined" \
  -H "Authorization: Bearer $API_KEY"
```

### Releasing a Quarantined Email

If you've reviewed an email and it's safe to release:

```bash
curl -X POST https://chela.email/api/emails/EMAIL_ID/release \
  -H "Authorization: Bearer $API_KEY"
```

Returns `{"success": true}` on success. Fires an `email.received` webhook event.

### Webhook Events

Subscribe to `email.quarantined` to be notified in real-time:

```bash
curl -X POST https://chela.email/api/notifiers \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transport": "webhook", "config": {"url": "https://your-server.com/webhook"}, "event_types": ["email.quarantined"]}'
```

The `email.quarantined` payload includes `injection_confidence` and `injection_explanation` fields.

### Auto-Quarantine Settings

Auto-quarantine is enabled by default with a confidence threshold of `0.8`. When analysis fails (e.g., LLM unavailable), emails are delivered normally without quarantine.

---

## Error Handling

All errors return JSON with an `error` field:

```json
{"error": "Description of the problem"}
```

| Status | Meaning |
|--------|---------|
| 400 | Bad request (validation error) |
| 401 | Missing or invalid API key |
| 403 | Insufficient scope or unauthorized resource |
| 404 | Resource not found |
| 409 | Conflict (duplicate resource) |
| 429 | Rate limited |
| 500 | Internal server error |
| 502 | Upstream delivery failure |

---

## Sending Email (Paid Feature)

Free accounts can receive email. To send, either register with a `discount_code` for free outbound credits, buy pay-as-you-go credits, or activate a recurring subscription.

### Option A: Stripe (for humans)

Request an upgrade URL and give it to the account owner to open in their browser:

```bash
curl -X POST https://chela.email/api/payments/stripe/upgrade-url \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"blocks": 1}'
```

Response:
```json
{
  "url": "https://checkout.stripe.com/c/pay/...",
  "blocks": 1,
  "monthly_sends": 500,
  "price_per_month": 5.0
}
```

The human opens the URL, completes checkout, and the account is upgraded automatically.

### Option B: Pay-as-you-go credits via x402 (for agents)

Buy a non-expiring 500-credit pack. This endpoint uses the [x402 HTTP payment protocol](https://www.x402.org/).

**How x402 works:**

1. Send `POST /api/billing/credits` with your auth header.
2. The server returns `402 Payment Required` with payment requirements in the response body (price, network, pay-to address).
3. Resolve the payment using the x402 protocol — sign and construct a valid `X-PAYMENT` header containing the payment payload. **Do not send USDC directly to the treasury address** — the server will not recognize raw transfers.
4. Retry the same `POST /api/billing/credits` request, this time including the `X-PAYMENT` header.
5. The server verifies the payment via the x402 facilitator, grants credits, and returns a `200` response.

```bash
# Step 1: Initial request returns 402 with payment requirements
curl -X POST https://chela.email/api/billing/credits \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"packs": 1}'

# Step 2: After resolving payment, retry with X-PAYMENT header
curl -X POST https://chela.email/api/billing/credits \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-PAYMENT: <x402-payment-payload>" \
  -d '{"packs": 1}'
```

Client libraries like `@coinbase/x402` handle steps 2-4 automatically.

### Option C: Recurring USDC on Base (for agents)

1. Register with `POST /api/auth/register`
2. Use your wallet provider to call `requestSpendPermission()`
3. Submit the granted permission response to `POST /api/subscriptions/grant`
4. Check `GET /api/subscriptions/current` until the account is active
5. When you want to stop future renewals, call `POST /api/subscriptions/cancel`

Example grant request:

```bash
curl -X POST https://chela.email/api/subscriptions/grant \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "blocks": 2,
    "agent_wallet_address": "0xAgentWallet",
    "guardian_wallet_address": "0xGuardianWallet",
    "permission": {
      "chainId": "0x14a34",
      "permissionHash": "0x1111111111111111111111111111111111111111111111111111111111111111",
      "signature": "0x2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222",
      "permission": {
        "account": "0xAgentWallet",
        "spender": "0xChelaMailSpender",
        "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        "allowance": "10000000",
        "period": 2592000,
        "start": 1773360000,
        "end": 1775952000,
        "salt": "1",
        "extraData": "0x"
      }
    }
  }'
```

### Pricing

Each recurring block = 500 outbound emails/month. Pay-as-you-go packs also grant 500 outbound credits, but credits do not expire. Inbound email is always free and unlimited.

| Tier | Outbound Sends | Inbound | Price |
|------|---------------|---------|-------|
| Free | 0 (receive only) | Unlimited | Free |
| Paygo credits | 500 non-expiring credits per pack | Unlimited | $8 per pack |
| Recurring subscription | 500 per block / month | Unlimited | $5/month per block |

---

## Discovery

Service info and endpoint listing available without auth:

```bash
curl https://chela.email/api/discovery
```

## Support

Contact support via the authenticated API endpoint:

```bash
curl -X POST https://chela.email/api/support \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Help needed", "message": "Describe your issue here"}'
```

Rate limited to 5 requests per day per account.

## References

- [REST API Reference](references/api-reference.md) — Full request/response contracts
- [MCP Tools Reference](references/mcp-tools.md) — Tool parameter schemas and examples
- [Webhooks Reference](references/webhooks.md) — Events, HMAC verification, retry policy
- [Terms of Service](https://chela.email/terms)
