Quickstart
You need an account and an API key. Sign up at /app/signup, verify your email, then create a key under API keys. The secret is shown once.
export BASE_URL="http://localhost:3000"
export API_KEY="am_..."
1. Create an inbox#
curl -X POST "$BASE_URL/v0/inboxes" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "support-agent", "display_name": "Support Agent"}'
The response includes inbox_id and email. Omit username to get a random address. A repeated client_id returns the inbox you already created instead of a duplicate, which makes retries safe.
2. Send a message#
curl -X POST "$BASE_URL/v0/inboxes/$INBOX_ID/messages/send" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: welcome-001" \
-d '{"to": "you@example.com", "subject": "Hello", "text": "Sent by an agent."}'
The response is { "message_id": "...", "thread_id": "..." }. Reusing the same Idempotency-Key on the same path replays the first response and adds an Idempotent-Replayed: true header.
3. Read the reply#
curl "$BASE_URL/v0/inboxes/$INBOX_ID/messages?labels=received&limit=10" \
-H "Authorization: Bearer $API_KEY"
Replies land in the same thread. Use extracted_text for the new text without the quoted chain. To be told instead of polling, create a webhook or open a WebSocket.
The same with the TypeScript SDK#
The SDK in the repository's sdk/ folder has no runtime dependencies and needs Node 22 or newer. It is not published to npm yet: build it (npm run build inside sdk/) and install it from the folder. You must pass baseUrl, because the default points at the hosted service.
import { EmailingAgentsClient } from "emailingagents-sdk";
const client = new EmailingAgentsClient({ apiKey: process.env.API_KEY!, baseUrl: process.env.BASE_URL! });
const inbox = await client.inboxes.create({ username: "support-agent" });
await client.inboxes.messages.send(
inbox.inbox_id,
{ to: "you@example.com", subject: "Hello", text: "Sent by an agent." },
{ idempotencyKey: "welcome-001" },
);
const { messages } = await client.inboxes.messages.list(inbox.inbox_id, { labels: ["received"], limit: 10 });
console.log(messages[0]?.extracted_text);
Trying it without real mail#
Outside production, POST /v0/_dev/inbound?to=<address> accepts a raw RFC 822 body (Content-Type: text/plain) and delivers it as if it had arrived. This endpoint is not available in production.