Inbound: Programmable Email Infrastructure With Its Own AWS Stack

January 19, 2026

|repo-review

by Florian Narr

Inbound: Programmable Email Infrastructure With Its Own AWS Stack

What it does

Inbound is email infrastructure for developers. You register domains, create email addresses via API, and incoming mail gets parsed and forwarded to your webhooks. Outgoing mail works too — send, reply, schedule. The whole thing runs on AWS SES with a Lambda processor and S3 storage underneath.

Why I starred it

Most email APIs abstract the infrastructure away entirely. Inbound does the opposite — it ships the AWS CDK stack alongside the application code. You can read exactly how emails flow from SES receipt rules through S3 to a Lambda that forwards parsed content to your webhook endpoint. That transparency is rare in email infrastructure projects.

The CLI is the other thing that caught my eye. A proper inbound CLI with mailbox management, draft files, and a custom .inbound draft format that reads like email headers. Someone thought about the developer workflow beyond just REST endpoints.

How it works

The architecture has three layers: AWS infrastructure, a Next.js API, and a TypeScript SDK with CLI.

The email pipeline

The CDK stack in aws/cdk/lib/inbound-email-stack.ts sets up the full receive path: SES receipt rules catch incoming mail, dump raw content to S3, and trigger a Lambda function. The Lambda in aws/cdk/lib/lambda/email-processor.ts does the heavy lifting — it fetches the raw email from S3, processes each SES record, and forwards everything to the application webhook.

The S3 lookup logic is worth reading. When SES doesn't provide an object key (which happens), the Lambda tries multiple paths:

// emails/batch-catchall/{messageId} — most common
// emails/{domain}/{messageId} — individual rule
// emails/{domain}/catchall/{messageId} — catch-all rule
const possibleKeys: string[] = [];
possibleKeys.push(`emails/batch-catchall/${messageId}`);
for (const domain of domains) {
  possibleKeys.push(
    `emails/${domain}/${messageId}`,
    `emails/${domain}/catchall/${messageId}`
  );
}

It tries each location sequentially and bails when it finds the content. There's also a 3.5MB size check — emails larger than that skip the webhook payload and the app fetches from S3 directly.

The API layer

The API at app/api/e2/[[...slugs]]/route.ts uses Elysia (a Bun-native web framework) mounted inside Next.js route handlers. Every HTTP method maps to the same app.fetch:

export const GET = app.fetch;
export const POST = app.fetch;
export const PUT = app.fetch;
export const PATCH = app.fetch;
export const DELETE = app.fetch;

This is an unusual pattern — running Elysia inside Next.js App Router catch-all routes. It gives them OpenAPI spec generation for free via @elysiajs/openapi while keeping the deployment on Vercel. The API covers domains, email addresses, endpoints, emails, threads, attachments, and a "guard" system for email filtering rules.

The CLI and draft format

The CLI in packages/inbound-cli/src/cli.ts parses commands into three groups: mailbox (local config), draft (composing), and everything else (API calls). The mailbox system is local-first — you configure named mailboxes in a config file with addresses and domains, then commands automatically scope to the active mailbox.

The .inbound draft format in packages/inbound-cli/src/lib/draft.ts is essentially a simplified email with headers and a body:

From: Support <support@yourdomain.com>
To: user@example.com
Subject: Your subject here
Content-Type: text/plain

Hello, your message here.

The parser validates headers against a whitelist, handles multi-value headers for to, cc, bcc, and supports both text/plain and text/html content types. Then inbound send draft.inbound ships it.

Security agent

There's also a packages/security-agent that wraps OpenCode (an AI coding agent SDK) behind a Bun HTTP server. It accepts commands via POST, creates OpenCode sessions, and runs them with full permissions. Presumably used for AI-powered email filtering or abuse detection — the guard system in the API hints at this.

Using it

npm install inboundemail

# SDK usage
import { Inbound } from 'inboundemail'
const inbound = new Inbound(process.env.INBOUND_API_KEY)

# Add a domain
const domain = await inbound.domains.create({ domain: "yourdomain.com" })

# Create an address with webhook
const addr = await inbound.emailAddresses.create({
  address: "hello@yourdomain.com",
  webhookUrl: "https://yourapp.com/webhook/email"
})

# Send an email
await inbound.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<p>Thanks for signing up!</p>'
})

The CLI supports the same operations plus local mailbox management and the draft workflow.

Rough edges

The dependency list is substantial — over 80 production dependencies including @ai-sdk/openai, @monaco-editor/react, framer-motion, recharts, and react-player. This is clearly the full SaaS app, not a lean library. The npm package inboundemail is presumably a separate, trimmed SDK, but the repo itself is a monorepo that mixes the hosted platform with the open-source tooling.

The repo has a single squashed commit, so there's no git history to read. Test coverage exists but is scattered — about a dozen test files covering the CLI, API routes, and some utility functions. No CI configuration is visible in the repo.

The replyToEmail function in functions/mail/primary.ts is still a placeholder returning status: 'pending_implementation'. The security agent has a wildcard permission rule ({ permission: "*", pattern: "*", action: "allow" }) which is a broad default for anything running AI commands.

Documentation lives in internal-docs and some inline OpenAPI descriptions, but there's no standalone contributor guide or architecture doc.

Bottom line

Inbound is useful if you need programmable inbound email processing and want to understand (or control) the AWS infrastructure underneath. The CLI and draft system show thoughtful DX work. Best suited for developers building agent workflows, support systems, or any app that needs to receive and react to email programmatically.

inboundemail/inbound on GitHub
inboundemail/inbound