API Documentation

Everything you need to send and receive emails programmatically with MailboxKit.

Quick Start

Get your AI agent sending emails in 3 steps — no human signup required:

1

Register and get your API key

One POST to self-register. You get an API key, email address, and inbox — all in one response.

Self-register
curl -X POST https://mailboxkit.com/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "owner_email": "you@company.com"}'

Prefer a dashboard? Sign up here instead.

2

Send an email

Send email
curl -X POST https://mailboxkit.com/api/v1/inboxes/{inbox_id}/messages/send \
  -H "Authorization: Bearer mbk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["customer@example.com"],
    "subject": "Hello from your AI agent!",
    "text": "This email was sent programmatically."
  }'
3

Set up a webhook for replies

Create webhook
curl -X POST https://mailboxkit.com/api/v1/webhooks \
  -H "Authorization: Bearer mbk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/email",
    "events": ["message.received"]
  }'

Self-Registration

AI agents can register themselves with a single API call — no human signup, no dashboard, no OAuth. The response includes everything needed to start sending email immediately.

POST /api/v1/register
curl -X POST https://mailboxkit.com/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "owner_email": "human-owner@example.com"}'

Response (201):

Response
{
  "api_key": "mbk_xxxxxxxxxx...",
  "email": "abc123@agent.mailboxkit.com",
  "inbox_id": "01JXYZ...",
  "owner_email": "human-owner@example.com",
  "message": "Save your API key — it will not be shown again."
}

Parameters: name (required) — agent name. owner_email (required) — human owner's email for account claiming.

Rate limit: 5 registrations per IP per hour, 3 per owner email per hour.

No auth required. The API key is shown only once — save it immediately.

For a complete agent-oriented guide, see skill.md.

Authentication

All API requests (except self-registration) require a Bearer token in the Authorization header. API keys are prefixed with mbk_.

Authorization header
Authorization: Bearer mbk_your_api_key

Get an API key via self-registration, the dashboard, or the API Keys endpoint. Each key is scoped to an organization.

Node.js SDK

The official Node.js SDK provides a typed, promise-based interface to the MailboxKit API.

Install
npm install mailboxkit
Basic usage
import MailboxKit from "mailboxkit";

// Reads MAILBOXKIT_API_KEY from env automatically
const mbk = new MailboxKit();

// Or pass the key explicitly
const mbk = new MailboxKit({ apiKey: "mbk_your_api_key" });

// Send an email
const msg = await mbk.send(inboxId, {
  to: ["recipient@example.com"],
  subject: "Hello from the SDK",
  html: "<p>Sent with the Node.js SDK</p>",
});

// Reply to a message
await mbk.reply(inboxId, msg.id, {
  text: "Thanks for your reply!",
});

// Wait for a matching email (polls until found or timeout)
const received = await mbk.waitFor(inboxId, {
  from: "sender@example.com",
  subject: /order confirmation/i,
  timeout: 30_000,
});

// Register a new organization
const reg = await mbk.register({
  name: "My Agent",
  owner_email: "owner@company.com",
});

The SDK includes full TypeScript type definitions. It reads MAILBOXKIT_API_KEY and MAILBOXKIT_INBOX_ID from environment variables by default.

MCP Server

The MailboxKit MCP server lets AI coding assistants (like Claude Code) send and receive emails through the Model Context Protocol. Add it to your MCP client configuration:

MCP configuration (e.g. Claude Code)
{
  "mcpServers": {
    "mailboxkit": {
      "command": "npx",
      "args": ["-y", "mailboxkit-mcp"],
      "env": {
        "MAILBOXKIT_API_KEY": "mbk_your_api_key",
        "MAILBOXKIT_INBOX_ID": "your_default_inbox_id"
      }
    }
  }
}

Available Tools

ToolDescription
send_emailSend an email from an inbox
check_inboxList recent messages in an inbox
read_messageRead a specific message by ID
reply_to_emailReply to an existing message
wait_for_emailPoll for a matching email until found or timeout
list_inboxesList all inboxes in the organization
create_inboxCreate a new inbox
search_messagesSearch messages across all inboxes

Inboxes

POST/api/v1/inboxes

Create a new email inbox.

Request body
{
  "name": "Support Agent",
  "email": "support@yourdomain.com"
}
Response (201)
{
  "data": {
    "id": 1,
    "name": "Support Agent",
    "email": "support@yourdomain.com",
    "organization_id": 1,
    "created_at": "2026-02-20T12:00:00Z"
  }
}
GET/api/v1/inboxes

List all inboxes in your organization.

GET/api/v1/inboxes/{inbox_id}

Get a specific inbox by ID.

DELETE/api/v1/inboxes/{inbox_id}

Delete an inbox and all its messages.

Messages

POST/api/v1/inboxes/{inbox_id}/messages/send

Send an email from an inbox. Supports optional file attachments via JSON (base64) or multipart upload.

Request body (JSON)
{
  "to": ["recipient@example.com"],
  "cc": ["cc@example.com"],
  "bcc": ["bcc@example.com"],
  "subject": "Hello!",
  "text": "Plain text body",
  "html": "<p>HTML body</p>",
  "attachments": [
    {
      "filename": "report.pdf",
      "content": "JVBERi0xLjQK...",
      "content_type": "application/pdf"
    }
  ]
}
Request (multipart form-data)
curl -X POST https://mailboxkit.com/api/v1/inboxes/1/messages/send \
  -H "Authorization: Bearer mbk_your_api_key" \
  -F "to[0]=recipient@example.com" \
  -F "subject=Hello!" \
  -F "html=<p>HTML body</p>" \
  -F "attachments[0]=@report.pdf" \
  -F "attachments[1]=@image.png"

Attachment limits: max 5 files, 10 MB each. Each attachment costs $0.001 in addition to the $0.002 per-email fee.

Response (201)
{
  "data": {
    "id": 1,
    "inbox_id": 1,
    "from_email": "support@yourdomain.com",
    "to_emails": ["recipient@example.com"],
    "subject": "Hello!",
    "status": "queued",
    "attachments": [
      {
        "id": 1,
        "filename": "report.pdf",
        "mime_type": "application/pdf",
        "size": 24580
      }
    ],
    "created_at": "2026-02-20T12:00:00Z"
  }
}
POST/api/v1/inboxes/{inbox_id}/messages/{message_id}/reply

Reply to an existing message. Automatically maintains threading. Supports attachments (same format as send).

Request body
{
  "text": "Thanks for your reply!",
  "html": "<p>Thanks for your reply!</p>",
  "attachments": [
    {
      "filename": "receipt.pdf",
      "content": "JVBERi0xLjQK...",
      "content_type": "application/pdf"
    }
  ]
}
GET/api/v1/inboxes/{inbox_id}/messages

List messages in an inbox. Supports pagination.

GET/api/v1/inboxes/{inbox_id}/messages/{message_id}

Get a specific message with full content and attachments.

Message responses include an extracted_text field containing the plain-text content extracted from the HTML body, useful for AI agents that need clean text without HTML markup.

Response (200)
{
  "data": {
    "id": "01JXYZ...",
    "inbox_id": "01JABC...",
    "from_email": "sender@example.com",
    "to_emails": ["inbox@yourdomain.com"],
    "subject": "Re: Order #1234",
    "text_body": "Thanks, got it!",
    "html_body": "<p>Thanks, got it!</p>",
    "extracted_text": "Thanks, got it!",
    "status": "received",
    "thread_id": "01JDEF...",
    "created_at": "2026-02-20T12:00:00Z"
  }
}
POST/api/v1/messages/search

Search messages across all inboxes.

Request body
{
  "query": "order confirmation",
  "inbox_id": 1,
  "fields": ["subject", "body"],
  "date_from": "2026-01-01",
  "date_to": "2026-12-31"
}

Threads

Threads group related messages using email References and In-Reply-To headers.

GET/api/v1/inboxes/{inbox_id}/threads

List conversation threads in an inbox.

GET/api/v1/threads/{thread_id}

Get a thread with all its messages.

Webhooks

POST/api/v1/webhooks

Create a webhook subscription.

Request body
{
  "name": "Email Notifications",
  "url": "https://your-app.com/webhooks/email",
  "events": ["message.received", "message.sent", "message.delivered"]
}
GET/api/v1/webhooks

List all webhooks.

GET/api/v1/webhooks/{webhook_id}

Get a specific webhook.

DELETE/api/v1/webhooks/{webhook_id}

Delete a webhook.

POST/api/v1/webhooks/{webhook_id}/test

Send a test payload to a webhook URL.

GET/api/v1/webhooks/{webhook_id}/attempts

View delivery attempt logs for a webhook.

Domains

POST/api/v1/domains

Add a custom sending domain.

Request body
{
  "name": "yourdomain.com"
}
GET/api/v1/domains

List all domains.

GET/api/v1/domains/{domain_id}

Get domain details and verification status.

DELETE/api/v1/domains/{domain_id}

Delete a domain.

POST/api/v1/domains/{domain_id}/verify

Trigger DNS verification for a domain. Checks MX, SPF, DKIM, and DMARC records.

Response (200)
{
  "data": {
    "id": 1,
    "name": "yourdomain.com",
    "is_verified": true,
    "mx_verified": true,
    "spf_verified": true,
    "dkim_verified": true,
    "dmarc_verified": false,
    "verified_at": "2026-02-20T12:00:00Z"
  }
}
GET/api/v1/domains/{domain_id}/dns

Get the required DNS records for domain verification.

Response (200)
{
  "data": {
    "mx": { "type": "MX", "host": "@", "value": "mx.mailboxkit.com", "priority": 10 },
    "spf": { "type": "TXT", "host": "@", "value": "v=spf1 include:mailboxkit.com -all" },
    "dkim": { "type": "TXT", "host": "default._domainkey", "value": "v=DKIM1; k=rsa; p=..." },
    "dmarc": { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=none;" }
  }
}

API Keys

POST/api/v1/api_keys

Create a new API key.

Request body
{
  "name": "Production Key"
}
Response (201)
{
  "data": {
    "id": 1,
    "name": "Production Key",
    "key": "mbk_live_abc123...",
    "created_at": "2026-02-20T12:00:00Z"
  },
  "message": "Store this key securely. It will not be shown again."
}
GET/api/v1/api_keys

List all API keys (keys are masked).

GET/api/v1/api_keys/{key_id}

Get a specific API key.

DELETE/api/v1/api_keys/{key_id}

Revoke an API key.

Organization

GET/api/v1/organization

Get your organization profile and usage stats.

Response (200)
{
  "data": {
    "id": 1,
    "name": "My Org",
    "inboxes_count": 5,
    "messages_count": 1234,
    "domains_count": 2,
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Webhook Events

Subscribe to these events when creating a webhook. Payloads are JSON POST requests to your URL.

EventDescription
message.receivedNew email received in an inbox
message.sentEmail sent successfully (queued for delivery)
message.deliveredEmail confirmed delivered to recipient
message.bouncedEmail bounced (invalid address or mailbox full)
message.openedRecipient opened the email (tracking pixel)
message.complainedRecipient marked email as spam
message.rejectedEmail rejected by provider
inbox.createdNew inbox created
inbox.deletedInbox deleted

Payload format

Webhook payload
{
  "event": "message.received",
  "timestamp": "2026-02-20T12:00:00Z",
  "data": {
    "id": 42,
    "inbox_id": 1,
    "message_id": "<abc123@example.com>",
    "from_email": "sender@example.com",
    "to_emails": ["inbox@yourdomain.com"],
    "subject": "Re: Order #1234",
    "text_body": "Thanks, got it!",
    "html_body": "<p>Thanks, got it!</p>",
    "attachments": [
      {
        "id": 1,
        "filename": "receipt.pdf",
        "mime_type": "application/pdf",
        "size": 24580,
        "url": "https://r2.mailboxkit.com/attachments/..."
      }
    ],
    "thread_id": 5,
    "created_at": "2026-02-20T12:00:00Z"
  }
}

Webhook Signatures

Every webhook request includes an X-MailboxKit-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook's secret.

Verify signature (Python)
import hmac, hashlib

def verify_signature(payload_bytes, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload_bytes,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# In your webhook handler:
signature = request.headers["X-MailboxKit-Signature"]
is_valid = verify_signature(request.body, signature, WEBHOOK_SECRET)
Verify signature (Node.js)
const crypto = require("crypto");

function verifySignature(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// In your webhook handler:
const signature = req.headers["x-mailboxkit-signature"];
const isValid = verifySignature(req.rawBody, signature, WEBHOOK_SECRET);

Sandbox Mode

Test your integration without sending real emails or incurring charges. Create a test API key (prefixed with mbk_test_) to enable sandbox mode.

Create a test key
curl -X POST https://mailboxkit.com/api/v1/api_keys \
  -H "Authorization: Bearer mbk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Test Key", "test": true}'

How sandbox mode works

  • No emails are actually sent — messages are created and recorded but never delivered to recipients
  • No balance is charged — send as many test emails as you want without affecting your wallet
  • Messages are still created — you can list, read, and search test messages via the API just like real ones

Use sandbox mode for development, CI testing, and integration validation. Switch to a live key (mbk_live_) when you are ready to send real emails.

Idempotency

Prevent duplicate sends by including an Idempotency-Key header on POST requests. If the same key is reused within 24 hours, MailboxKit returns the original response instead of creating a duplicate.

Example request with idempotency key
curl -X POST https://mailboxkit.com/api/v1/inboxes/{inbox_id}/messages/send \
  -H "Authorization: Bearer mbk_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-abc123" \
  -d '{
    "to": ["recipient@example.com"],
    "subject": "Order confirmation",
    "text": "Your order has been confirmed."
  }'

When a replayed response is returned, the response includes an X-Idempotent-Replayed: true header so you can distinguish replays from original requests.

  • Keys are cached for 24 hours, then expire automatically
  • Keys are scoped to your API key — different API keys can reuse the same idempotency key
  • Only POST endpoints support idempotency keys (GET, DELETE are naturally idempotent)

Rate Limits

API requests are rate limited per API key:

  • 1,000 requests per minute
  • 50,000 requests per day

Rate limit info is included in response headers:

Response headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1708430400

When rate limited, the API returns 429 Too Many Requests with a Retry-After header indicating seconds to wait.

Billing & Errors

MailboxKit uses a prepaid wallet model. Each email costs $0.002 and each attachment costs $0.001. New accounts receive $1.00 in free credit (500 emails).

Insufficient Balance (402)

If your balance is too low to send an email, the /send and /reply endpoints return 402 Payment Required:

402 Response
{
  "error": "insufficient_balance",
  "message": "Your organization does not have enough credit to send this email.",
  "action": "Top up your wallet to continue sending.",
  "billing_url": "https://mailboxkit.com/billing",
  "docs_url": "https://mailboxkit.com/api-docs#billing-errors",
  "balance_mills": 0,
  "balance_dollars": "$0.000",
  "cost_mills": 2,
  "cost_dollars": "$0.002",
  "emails_remaining": 0
}

When this happens, the account owner receives an email notification with a link to top up. To handle this in your agent:

  • Check for HTTP status 402 on send/reply calls
  • Notify the user or log that credits need to be added
  • Receiving emails and reading messages are not affected by balance
  • Top up at mailboxkit.com/billing to resume sending

Common Error Codes

StatusMeaningAction
401Invalid or missing API keyCheck your Bearer token
402Insufficient balanceSee action field; top up at billing_url
403Access denied to resourceVerify inbox/message ownership
422Validation errorCheck request body fields
429Rate limitedWait and retry (see Retry-After header)

Integration Examples

Examples for popular AI agent frameworks.

Node.js SDK

Node.js SDK integration
import MailboxKit from "mailboxkit";

const mbk = new MailboxKit(); // reads MAILBOXKIT_API_KEY from env

// Send an email
const message = await mbk.send(inboxId, {
  to: ["customer@example.com"],
  subject: "Your order has shipped",
  html: "<p>Your package is on the way!</p>",
});
console.log("Sent:", message.id);

// Wait for a reply (polls with timeout)
const reply = await mbk.waitFor(inboxId, {
  subject: /re: your order/i,
  timeout: 60_000,
});
console.log("Got reply:", reply.extracted_text);

// Reply back
await mbk.reply(inboxId, reply.id, {
  text: "Thanks for confirming!",
});

MCP Server

With the MCP server configured, AI assistants can send and read emails directly. No code needed — just add the config and start chatting.

MCP configuration
// Add to your MCP client settings (e.g. .mcp.json)
{
  "mcpServers": {
    "mailboxkit": {
      "command": "npx",
      "args": ["-y", "mailboxkit-mcp"],
      "env": {
        "MAILBOXKIT_API_KEY": "mbk_your_api_key",
        "MAILBOXKIT_INBOX_ID": "your_default_inbox_id"
      }
    }
  }
}

// Then ask your AI assistant:
// "Send an email to bob@example.com about the meeting tomorrow"
// "Check my inbox for new messages"
// "Reply to the latest email from alice@example.com"

OpenAI Function Calling

Tool definition
{
  "type": "function",
  "function": {
    "name": "send_email",
    "description": "Send an email via MailboxKit",
    "parameters": {
      "type": "object",
      "properties": {
        "to": { "type": "string", "description": "Recipient email address" },
        "subject": { "type": "string", "description": "Email subject" },
        "body": { "type": "string", "description": "Email body (HTML)" }
      },
      "required": ["to", "subject", "body"]
    }
  }
}
Tool implementation (Python)
import requests

MAILBOXKIT_API_KEY = "mbk_your_api_key"
INBOX_ID = 1

def send_email(to, subject, body):
    response = requests.post(
        f"https://mailboxkit.com/api/v1/inboxes/{INBOX_ID}/messages/send",
        headers={
            "Authorization": f"Bearer {MAILBOXKIT_API_KEY}",
            "Content-Type": "application/json",
        },
        json={"to": [to], "subject": subject, "html": body},
    )
    return response.json()

LangChain Tool

LangChain integration
from langchain.tools import tool
import requests

MAILBOXKIT_API_KEY = "mbk_your_api_key"
INBOX_ID = 1

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a recipient via MailboxKit."""
    response = requests.post(
        f"https://mailboxkit.com/api/v1/inboxes/{INBOX_ID}/messages/send",
        headers={
            "Authorization": f"Bearer {MAILBOXKIT_API_KEY}",
            "Content-Type": "application/json",
        },
        json={"to": [to], "subject": subject, "html": body},
    )
    data = response.json()
    return f"Email sent (ID: {data['data']['id']})"

CrewAI Agent

CrewAI integration
from crewai import Agent, Task
from langchain.tools import tool
import requests

MAILBOXKIT_API_KEY = "mbk_your_api_key"
INBOX_ID = 1

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a recipient."""
    response = requests.post(
        f"https://mailboxkit.com/api/v1/inboxes/{INBOX_ID}/messages/send",
        headers={
            "Authorization": f"Bearer {MAILBOXKIT_API_KEY}",
            "Content-Type": "application/json",
        },
        json={"to": [to], "subject": subject, "html": body},
    )
    return response.json()

@tool
def check_inbox() -> str:
    """Check for new emails in the inbox."""
    response = requests.get(
        f"https://mailboxkit.com/api/v1/inboxes/{INBOX_ID}/messages",
        headers={"Authorization": f"Bearer {MAILBOXKIT_API_KEY}"},
    )
    messages = response.json()["data"]
    return f"Found {len(messages)} messages"

support_agent = Agent(
    role="Customer Support Agent",
    goal="Handle customer emails professionally",
    tools=[send_email, check_inbox],
)

OpenClaw Skill

Create a skill folder at ~/.openclaw/skills/mailboxkit/ with a SKILL.md file:

~/.openclaw/skills/mailboxkit/SKILL.md
---
name: mailboxkit
description: Send and receive emails via MailboxKit API
requires:
  env:
    - MAILBOXKIT_API_KEY
    - MAILBOXKIT_INBOX_ID
  bins:
    - curl
    - jq
---

# MailboxKit Email Skill

You can send and check emails using the MailboxKit API.

## Send an email

When the user asks you to send an email, use:

```bash
curl -s -X POST "https://mailboxkit.com/api/v1/inboxes/$MAILBOXKIT_INBOX_ID/messages/send" \
  -H "Authorization: Bearer $MAILBOXKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["RECIPIENT"],
    "subject": "SUBJECT",
    "html": "BODY"
  }' | jq .
```

Replace RECIPIENT, SUBJECT, and BODY with the values from the conversation.

## Check inbox for new messages

```bash
curl -s "https://mailboxkit.com/api/v1/inboxes/$MAILBOXKIT_INBOX_ID/messages" \
  -H "Authorization: Bearer $MAILBOXKIT_API_KEY" | jq '.data[:5]'
```

## Reply to a message

When replying to a specific message (you'll need the message ID):

```bash
curl -s -X POST "https://mailboxkit.com/api/v1/inboxes/$MAILBOXKIT_INBOX_ID/messages/MESSAGE_ID/reply" \
  -H "Authorization: Bearer $MAILBOXKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "REPLY_BODY",
    "html": "<p>REPLY_BODY</p>"
  }' | jq .
```

## Search messages

```bash
curl -s -X POST "https://mailboxkit.com/api/v1/messages/search" \
  -H "Authorization: Bearer $MAILBOXKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "SEARCH_TERM"}' | jq '.data[:5]'
```