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:
Register and get your API key
One POST to self-register. You get an API key, email address, and inbox — all in one response.
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.
Send an 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."
}'Set up a webhook for replies
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.
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):
{
"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: Bearer mbk_your_api_keyGet 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.
npm install mailboxkitimport 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:
{
"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
| Tool | Description |
|---|---|
send_email | Send an email from an inbox |
check_inbox | List recent messages in an inbox |
read_message | Read a specific message by ID |
reply_to_email | Reply to an existing message |
wait_for_email | Poll for a matching email until found or timeout |
list_inboxes | List all inboxes in the organization |
create_inbox | Create a new inbox |
search_messages | Search messages across all inboxes |
Inboxes
/api/v1/inboxesCreate a new email inbox.
{
"name": "Support Agent",
"email": "support@yourdomain.com"
}{
"data": {
"id": 1,
"name": "Support Agent",
"email": "support@yourdomain.com",
"organization_id": 1,
"created_at": "2026-02-20T12:00:00Z"
}
}/api/v1/inboxesList all inboxes in your organization.
/api/v1/inboxes/{inbox_id}Get a specific inbox by ID.
/api/v1/inboxes/{inbox_id}Delete an inbox and all its messages.
Messages
/api/v1/inboxes/{inbox_id}/messages/sendSend an email from an inbox. Supports optional file attachments via JSON (base64) or multipart upload.
{
"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"
}
]
}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.
{
"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"
}
}/api/v1/inboxes/{inbox_id}/messages/{message_id}/replyReply to an existing message. Automatically maintains threading. Supports attachments (same format as send).
{
"text": "Thanks for your reply!",
"html": "<p>Thanks for your reply!</p>",
"attachments": [
{
"filename": "receipt.pdf",
"content": "JVBERi0xLjQK...",
"content_type": "application/pdf"
}
]
}/api/v1/inboxes/{inbox_id}/messagesList messages in an inbox. Supports pagination.
/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.
{
"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"
}
}/api/v1/messages/searchSearch messages across all inboxes.
{
"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.
/api/v1/inboxes/{inbox_id}/threadsList conversation threads in an inbox.
/api/v1/threads/{thread_id}Get a thread with all its messages.
Webhooks
/api/v1/webhooksCreate a webhook subscription.
{
"name": "Email Notifications",
"url": "https://your-app.com/webhooks/email",
"events": ["message.received", "message.sent", "message.delivered"]
}/api/v1/webhooksList all webhooks.
/api/v1/webhooks/{webhook_id}Get a specific webhook.
/api/v1/webhooks/{webhook_id}Delete a webhook.
/api/v1/webhooks/{webhook_id}/testSend a test payload to a webhook URL.
/api/v1/webhooks/{webhook_id}/attemptsView delivery attempt logs for a webhook.
Domains
/api/v1/domainsAdd a custom sending domain.
{
"name": "yourdomain.com"
}/api/v1/domainsList all domains.
/api/v1/domains/{domain_id}Get domain details and verification status.
/api/v1/domains/{domain_id}Delete a domain.
/api/v1/domains/{domain_id}/verifyTrigger DNS verification for a domain. Checks MX, SPF, DKIM, and DMARC records.
{
"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"
}
}/api/v1/domains/{domain_id}/dnsGet the required DNS records for domain verification.
{
"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
/api/v1/api_keysCreate a new API key.
{
"name": "Production Key"
}{
"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."
}/api/v1/api_keysList all API keys (keys are masked).
/api/v1/api_keys/{key_id}Get a specific API key.
/api/v1/api_keys/{key_id}Revoke an API key.
Organization
/api/v1/organizationGet your organization profile and usage stats.
{
"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.
| Event | Description |
|---|---|
message.received | New email received in an inbox |
message.sent | Email sent successfully (queued for delivery) |
message.delivered | Email confirmed delivered to recipient |
message.bounced | Email bounced (invalid address or mailbox full) |
message.opened | Recipient opened the email (tracking pixel) |
message.complained | Recipient marked email as spam |
message.rejected | Email rejected by provider |
inbox.created | New inbox created |
inbox.deleted | Inbox deleted |
Payload format
{
"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.
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)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.
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.
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:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1708430400When 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:
{
"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
| Status | Meaning | Action |
|---|---|---|
| 401 | Invalid or missing API key | Check your Bearer token |
| 402 | Insufficient balance | See action field; top up at billing_url |
| 403 | Access denied to resource | Verify inbox/message ownership |
| 422 | Validation error | Check request body fields |
| 429 | Rate limited | Wait and retry (see Retry-After header) |
Integration Examples
Examples for popular AI agent frameworks.
Node.js SDK
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.
// 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
{
"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"]
}
}
}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
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
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:
---
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]'
```