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.