Send and receive email with AI agents using the Commune API.
Commune gives AI agents a real email address and manages the full email lifecycle:
- Send email — DKIM-signed delivery, rate limiting, bounce tracking
- Receive email — webhooks with parsed content, spam scoring, prompt injection detection
- Thread email — automatic conversation threading via RFC 5322 Message-ID headers
- Extract structured data — JSON schema → LLM extraction on every inbound email
- Manage attachments — upload, send, download files with antivirus scanning
- Monitor deliverability — bounce rates, complaint rates, suppression lists via API
https://api.commune.email
All requests require a Bearer token:
Authorization: Bearer comm_...
Get an API key at https://commune.email/dashboard/api-keys
Alternatively, use agent authentication (Ed25519 challenge-response) to register and authenticate without human intervention. See https://commune.email/agent-auth
curl -X POST https://api.commune.email/v1/inboxes \
-H "Authorization: Bearer comm_..." \
-H "Content-Type: application/json" \
-d '{"local_part": "support"}'Response:
{
"data": {
"id": "inbox_abc123",
"localPart": "support",
"address": "support@commune.email",
"domainId": "d_xyz"
}
}curl -X POST https://api.commune.email/v1/messages/send \
-H "Authorization: Bearer comm_..." \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"subject": "Hello",
"text": "Message from my agent.",
"html": "<p>Message from my agent.</p>"
}'Response:
{
"data": {
"id": "msg_xyz",
"thread_id": "thread_abc"
}
}Set a webhook on the inbox:
curl -X PUT https://api.commune.email/v1/domains/DOMAIN_ID/inboxes/INBOX_ID \
-H "Authorization: Bearer comm_..." \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"endpoint": "https://your-server.com/webhook/email",
"events": ["inbound"]
}
}'Webhook payload:
{
"domainId": "d_abc",
"inboxId": "inbox_xyz",
"inboxAddress": "support@commune.email",
"message": {
"message_id": "msg_001",
"thread_id": "thread_abc",
"direction": "inbound",
"content": "Hi, I need help.",
"participants": [
{ "role": "sender", "identity": "user@example.com" },
{ "role": "to", "identity": "support@commune.email" }
],
"metadata": {
"subject": "Re: Hello",
"created_at": "2026-02-24T10:00:00Z"
},
"attachments": []
},
"security": {
"spam": { "checked": true, "score": 1.2, "flagged": false },
"prompt_injection": { "checked": true, "detected": false, "risk_level": "none" }
}
}import { CommuneClient } from 'commune-ai';
const commune = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY });
// Create inbox
const inbox = await commune.inboxes.create({ localPart: 'support' });
// Send email
const result = await commune.messages.send({
to: 'user@example.com',
subject: 'Hello',
text: 'Message from my agent.',
});
// Reply in thread
await commune.messages.send({
to: 'user@example.com',
subject: 'Re: Hello',
text: 'Here is my reply.',
thread_id: result.thread_id,
});
// List threads
const { data: threads } = await commune.threads.list({ inbox_id: inbox.id });
// Read thread messages
const messages = await commune.threads.messages(threads[0].thread_id);from commune import CommuneClient
client = CommuneClient(api_key="comm_...")
# Create inbox
inbox = client.inboxes.create(local_part="support")
# Send email
result = client.messages.send(
to="user@example.com",
subject="Hello",
text="Message from my agent.",
)
# Reply in thread
client.messages.send(
to="user@example.com",
subject="Re: Hello",
text="Here is my reply.",
thread_id=result.thread_id,
)
# List threads
threads = client.threads.list(inbox_id=inbox.id)
# Read thread messages
messages = client.threads.messages(threads.data[0].thread_id)Add to Claude Desktop / Cursor / Windsurf config:
{
"mcpServers": {
"commune": {
"command": "uvx",
"args": ["commune-mcp"],
"env": {
"COMMUNE_API_KEY": "comm_..."
}
}
}
}| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/inboxes | Create an inbox |
| GET | /v1/inboxes | List all inboxes |
| GET | /v1/inboxes/:id | Get an inbox |
| PUT | /v1/domains/:domainId/inboxes/:id | Update inbox (webhook, extraction schema) |
| POST | /v1/messages/send | Send an email |
| GET | /v1/messages | List messages |
| GET | /v1/threads | List conversation threads |
| GET | /v1/threads/:id/messages | Get messages in a thread |
| POST | /v1/attachments/upload | Upload an attachment |
| GET | /v1/attachments/:id | Download an attachment |
| GET | /v1/domains | List domains |
| POST | /v1/domains | Add a custom domain |
Configure a JSON schema to automatically extract structured data from every inbound email:
await commune.inboxes.setExtractionSchema({
domainId: 'DOMAIN_ID',
inboxId: inbox.id,
schema: {
name: 'support_ticket',
description: 'Extract ticket details from customer emails',
enabled: true,
schema: {
type: 'object',
properties: {
order_number: { type: 'string', description: 'Order reference number' },
intent: {
type: 'string',
enum: ['billing', 'technical', 'general', 'complaint', 'cancellation'],
},
urgency: { type: 'string', enum: ['low', 'medium', 'high'] },
summary: { type: 'string', description: 'One-line summary' },
},
},
},
});Extracted data appears in message.metadata.extracted_data and in the extractedData field of webhook payloads.
Every inbound email is automatically:
- Spam-scored (0-10 scale, flagged if > 5)
- Prompt injection-detected (risk levels: none, low, medium, high, critical)
The security context is included in every webhook payload:
{
"security": {
"spam": {
"checked": true,
"score": 2.1,
"flagged": false,
"reasons": []
},
"prompt_injection": {
"checked": true,
"detected": true,
"risk_level": "high",
"patterns": ["system_override", "ignore_instructions"]
}
}
}Always check security.prompt_injection.risk_level before acting on inbound email content in agentic systems.
Webhooks are signed with HMAC-SHA256:
x-commune-signature: v1=5a3f2b8c...
x-commune-timestamp: 1708347600000
Verify: HMAC-SHA256(webhookSecret, "{timestamp}.{rawBody}")
For agents that need to register themselves without human intervention:
- Generate Ed25519 keypair locally
- POST to
/v1/auth/agent-registerwith public key and agent purpose - Server returns a natural-language challenge
- Sign the challenge response with private key
- POST to
/v1/auth/agent-verify— receiveagentIdand auto-provisioned inbox
Full spec: https://commune.email/agent-auth
| Plan | Daily emails | Inboxes |
|---|---|---|
| Free | 50 | 1 |
| Agent Pro | 500 | 5 |
| Business | 2,000 | 20 |
| Enterprise | Custom | Custom |
Requests: 10/second per API key. Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
{
"error": {
"message": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED"
}
}Common codes: RATE_LIMIT_EXCEEDED (429), INVALID_API_KEY (401), INBOX_NOT_FOUND (404), SENDING_PAUSED (503 — inbox health issue), PLAN_UPGRADE_REQUIRED (403).
- One inbox per agent or campaign — isolates sender reputation and reply routing
- Check prompt injection score before acting —
security.prompt_injection.risk_level - Use thread_id when replying — keeps conversations threaded correctly
- Store thread_id after sending — needed to associate future replies with the original send
- Monitor bounce rate — if > 5%, stop sending and review list quality
- Use extraction schemas — let Commune extract structured data, don't parse email body yourself
- Verify webhook signatures — all inbound email is authenticated via HMAC
- Start with shared domain — use commune.email domain for testing, add custom domain for production