Skip to content

guides

Programmable Inboxes for AI Agents and E2E Tests

July 9, 2026

#ai-agents#email-testing#inbox-api#webhooks#comparison
Programmable Inboxes for AI Agents and E2E Tests
Visual: Northstar Desk on okslop

The next useful email primitive is not another dashboard. It is an inbox your code can create, watch, read, and destroy.

Programmable inboxes sit between email testing tools and agent infrastructure. A browser test needs a one-off address so it can sign up for a service and read the OTP. An AI agent needs its own address so it can receive replies, process attachments, and act under a separate identity from any human. The mechanics are similar: create an address, receive mail, expose the messages to code, and keep the authority narrow.

This is where the market is moving. Nylas now talks about Agent Accounts as email and calendar identities owned by software. AgentMail is explicitly selling inboxes for AI agents. MailSlurp and Mailosaur have long served the testing side of the same problem. The shape is becoming obvious: a mailbox is no longer only a thing a person opens in a UI. It is also a resource a program can own.

The two jobs this feature has to do

There are two use cases that look different in the UI but share the same backend.

1. Throwaway inboxes for E2E tests

The test runner creates an address, signs up to a service, waits for mail, extracts an OTP or magic link, then expires the address.

const inbox = await tinbox.devInboxes.create({
  prefix: "signup",
  ttl: "15m",
});

await page.fill("input[name=email]", inbox.address);
await page.click("text=Create account");

const message = await tinbox.devInboxes.waitForMessage(inbox.id, {
  timeoutMs: 60_000,
  subjectIncludes: "verification",
});

const code = message.text.match(/\b\d{6}\b/)?.[0];
await page.fill("input[name=code]", code ?? "");

This is not fake email. It is a real mailbox, but scoped to a short-lived test identity. That matters when the thing being tested sends real confirmation mail through real infrastructure.

2. Long-lived inboxes for agents

The operator creates an address for the agent, grants a narrow token, and optionally attaches a webhook. The agent reads its own inbox without borrowing a human account.

tinbox dev-inbox create \
  --prefix onboarding-agent \
  --ttl 30d \
  --webhook https://agents.example.com/inbound/tinbox \
  --json
{
  "id": "ibx_01J...",
  "address": "onboarding-agent-k7m9q2x4@agents.example.com",
  "token": "tbx_...",
  "expiresAt": 1783540000000,
  "webhook": {
    "id": "wh_01J...",
    "signingSecret": "whsec_..."
  }
}

The token should default to read-only. If the agent needs to reply, grant that separately. The first version should make it easy to read and hard to send by accident.

The market map

These products overlap, but they do not all optimize for the same thing.

ProductWhat it is strongest atNotes for this feature
Nylas Agent AccountsManaged email and calendar identities for agentsNylas frames the mailbox and calendar as a software-owned identity. It is strong when you want a managed provider with calendar included.
AgentMailAPI-created inboxes for AI agentsDirectly maps to the agent-inbox idea: create inboxes by API, use threads, attachments, realtime events, custom domains, SDKs, and MCP.
MailSlurpProgrammable email and SMS for testing, routing, and monitoringStrong on QA: generated inboxes, OTP and MFA tests, webhooks, SDKs, load tests, and deliverability probes.
MailosaurEmail and SMS testing for QA teamsSimilar testing buyer: create test addresses, wait for emails, assert OTPs, inspect messages, integrate with test frameworks.
MailtrapEmail sandbox and delivery testingStrong for development/staging inspection, template QA, SMTP/API test capture, and production email delivery workflows.
MailgunSending plus inbound routingGood reference for inbound parse webhooks and routing. It is an email API platform, not an agent-specific inbox product.
PostmarkTransactional email with inbound processingGood when transactional delivery quality is the primary buyer need, with inbound processing as a supporting feature.
SendGridEnterprise email API with inbound parseBroad sender platform. Inbound parse webhooks cover receiving, but not a full agent-inbox product by themselves.
TinboxOwned inbox, sender, archive, tokens, webhooks, and agent tools on your Cloudflare accountThe wedge should be programmable inboxes you own: address, D1-stored mail, scoped token, webhook, CLI, SDK, and expiry.

The useful lesson from the field is that "email for agents" is not one product category yet. It is three categories colliding:

  1. Testing inboxes: MailSlurp, Mailosaur, Mailtrap.
  2. Agent identities: Nylas Agent Accounts, AgentMail.
  3. Email API platforms with inbound webhooks: Mailgun, Postmark, SendGrid.

Tinbox should not pretend those products are bad. Many are excellent for their lane. The Tinbox angle is ownership: the inbox, messages, tokens, webhooks, and audit trail live in the deployment owner's Cloudflare account.

What to learn from the other players

Good comparison pages do not flatten everyone into "us versus them." They document the product shape each player chose, because that tells you what the customer is buying.

Nylas Agent Accounts is the clearest comparable product for long-running software identities. The pitch is not just "receive email by API." It is "give software a communication identity" with email, calendar, and the same Nylas grant model teams already use. That matters for scheduling agents and BDR agents, where the mailbox and calendar belong together.

AgentMail is the closest pure-play agent inbox API. It is useful to study because it names the agent use cases directly: browser agents that need 2FA codes, document-processing agents that parse attachments, and customer-service agents that route inbound mail. Tinbox should be able to explain the same use cases, then differentiate on ownership and deployment model.

MailSlurp and Mailosaur show what QA teams expect: create isolated addresses in parallel, wait for a message, extract a code, assert links, and clean up. The waitForMessage method is not a nice-to-have in that world. It is the whole developer experience.

Mailtrap is a reminder that many teams first meet this problem through pre-production email quality. They want a safe place to inspect messages before customers see them. Tinbox dev inboxes should not over-index on agents and forget staging, CI, and release checks.

Mailgun, Postmark, and SendGrid prove that inbound parse webhooks are a known pattern, but they also show the gap. A parse webhook is not the same as a scoped inbox with its own token, expiry, message history, and agent-readable thread model. Tinbox should be precise about that distinction.

Use this checklist when documenting competitors:

  • Name their strongest use case first. Do not dunk on a product for not being built for Tinbox's exact lane.
  • Separate testing inboxes from agent identities. They share mechanics, but buyers judge them differently.
  • Call out managed versus owned infrastructure. This is the most honest Tinbox distinction.
  • Document calendar support separately. Nylas includes calendar in the Agent Accounts story; Tinbox should not imply parity until it exists.
  • Document SMS separately. MailSlurp and Mailosaur cover email plus SMS testing; Tinbox should not blur that.
  • Avoid claims about pricing, compliance, or reliability unless the source page says it plainly and the article links to it.

What Tinbox should ship

The primitive should be a bundled API, not a new parallel email stack.

const inbox = await tinbox.devInboxes.create({
  prefix: "signup-test",
  domain: "agents.example.com",
  ttl: "2h",
  webhookUrl: "https://example.com/tinbox/inbound",
  webhookEvents: ["message.received"],
});

Under the hood, it composes existing Tinbox pieces:

  • create an inbox and primary address
  • create an inbox-bound API key
  • grant the key only the role and scopes requested
  • create a webhook when requested
  • record expiry metadata
  • invalidate routing so mail starts landing immediately

The response should be explicit about what can never be recovered:

{
  "inbox": {
    "id": "ibx_01J...",
    "address": "signup-test-v7k9m2q4x8p3@agents.example.com"
  },
  "token": {
    "id": "key_01J...",
    "value": "tbx_...",
    "scopes": ["read"],
    "expiresAt": 1783540000000
  },
  "webhook": {
    "id": "wh_01J...",
    "signingSecret": "whsec_..."
  }
}

Token value and webhook signing secret are shown once. After that, the API can list prefixes and ids, not secrets.

CLI shape

The CLI should cover the common test and agent flows without making developers write tRPC calls by hand.

# Create a short-lived inbox for an E2E test.
tinbox dev-inbox create --ttl 15m --prefix signup --json

# Wait for the newest matching message.
tinbox dev-inbox wait ibx_01J... \
  --subject verification \
  --timeout 60s \
  --json

# Read recent messages with the returned token.
TINBOX_TOKEN=tbx_... tinbox threads --inbox ibx_01J... --json

# Create an agent inbox and attach a webhook immediately.
tinbox dev-inbox create \
  --prefix support-agent \
  --ttl 30d \
  --webhook https://agents.example.com/tinbox \
  --json

# Revoke token, disable webhook, deactivate address.
tinbox dev-inbox delete ibx_01J... --json

wait is important. Tests do not want a generic inbox list; they want "block until the verification email arrives or fail with useful JSON."

Webhook payload

When an inbox has a webhook, inbound mail should emit a signed event. Keep the payload small enough for routing and retrieval. The receiver can call back to read the full thread if needed.

{
  "id": "evt_01J...",
  "type": "message.received",
  "createdAt": 1783539123000,
  "inbox": {
    "id": "ibx_01J...",
    "address": "support-agent-k7m9q2x4@agents.example.com"
  },
  "message": {
    "id": "msg_01J...",
    "threadId": "thr_01J...",
    "from": "customer@example.com",
    "subject": "I need help with my invoice",
    "snippet": "Can you send me a copy of..."
  }
}

Sign the body with the webhook secret and include a timestamp header. The receiver should reject stale timestamps and invalid signatures.

Defaults that keep agents boring

The safe defaults matter more than the happy path.

  • Read-only token by default. A test inbox rarely needs to send.
  • TTL by default. A dev inbox without an explicit TTL should still expire, for example after 24 hours.
  • Long random local-part. Prefixes are for human scanning; entropy comes from a random suffix.
  • Dedicated subdomain. Prefer agents.example.com or test.example.com, not the company's main domain.
  • Rate limits per creator and per token. Inbox creation is cheap, so it needs quota.
  • No immediate address reuse. Expired addresses should stay reserved for a cooling-off window.
  • Webhook SSRF guard. Only allow safe HTTPS destinations, and reuse the same outbound URL checks used elsewhere.
  • No delete power in reader tokens. Reading mail and destroying mail are different capabilities.

The security reason is not theoretical. Email is hostile input. A message can contain prompt injection aimed at the agent that reads it. The platform has to make bad sends structurally hard, not just politely discouraged.

Where Tinbox should differ

Nylas is compelling if you want a managed email plus calendar provider. AgentMail is compelling if you want a hosted agent inbox API. MailSlurp is compelling if your buyer is QA and you also need SMS. Mailtrap is compelling if sandbox inspection and delivery testing are the center of gravity.

Tinbox should be for the team that wants the same programmable primitive, but in its own deployment:

  • messages stored in its own D1
  • attachments in its own R2
  • webhook secrets in its own database
  • API keys that pass through the same authorize() gate as humans
  • CLI and SDK flows for agents and tests
  • no separate vendor inbox database

That is a real trade-off. Tinbox asks you to run the stack. The payoff is that the agent's mailbox is not another SaaS account; it is a row in infrastructure you control.

The product method

The method name should say what it does:

tinbox.devInboxes.create()
tinbox.devInboxes.waitForMessage()
tinbox.devInboxes.delete()

devInboxes is better than throwawayInboxes because the same primitive serves tests and long-running agents. Expiration is a property, not the identity of the product.

Use inboxes.create for human/team mailboxes. Use devInboxes.create when software owns the address and gets a scoped token back.

That gives developers one clean mental model: create an inbox for the thing doing the work, give that thing the least token it needs, and let mail become an event stream.

Frequently asked questions

What is a programmable inbox?

A programmable inbox is an email address and mailbox you create, read, search, and delete from code. Instead of opening webmail, a test runner or agent calls an API, CLI, SDK, webhook, or MCP tool.

How is this different from a disposable email address?

A disposable address is usually for a human to avoid spam. A programmable inbox is for software: it has API reads, event delivery, scoped tokens, audit logs, and often custom domains.

Why do AI agents need their own inboxes?

An agent needs a real address when it must receive OTP codes, reply to customers, process attachments, or keep an audit trail under its own identity. Borrowing a human account mixes permissions, logs, and risk.

Should a test inbox be allowed to send?

Usually no. The default token should be read-only. Add draft or reply rights only when the workflow proves it needs them, and keep send behind scopes, rate limits, and review.