---
title: "Test Inboxes for AI Agents"
description: "Give an agent a fresh address per run, read OTP codes and confirmation links over the REST API, and keep agent mail out of real inboxes."
canonical_url: "https://smtp.dev/docs/guides/agents/"
last_updated: "2026-07-16T01:19:48.065Z"
---

An agent that exercises your signup flow needs to receive real email: the verification code, the confirmation link, the welcome message. In development that mail shouldn't land in your inbox, and the agent shouldn't be anywhere near production mailboxes. A sandbox domain gives every run its own address, and the API gives the agent a way to read what arrived.

## Setup

<steps level="3">

### Create a catch-all account on a dev domain

Username `*` on e.g. `dev.example.com` - [how catch-all works](/docs/features/account#catch-all-addresses). Every address on the domain is now deliverable without creating accounts.

### Create an API token for the agent

On the [API Keys](/tokens) page. The agent authenticates every request with the `X-API-KEY` header.

</steps>

## A Fresh Address per Run

Derive the address from the run, same as the [QA pattern](/docs/guides/catch-all):

```js
const address = `agent-${crypto.randomUUID()}@dev.example.com`
```

The agent signs up with it, and the mail lands in the catch-all's INBOX.

## Reading the OTP

`inboxOf` and `waitForMessage` are the polling helpers from the [CI guide](/docs/guides/ci). The catch-all holds mail for the whole domain, so match on the recipient:

```js
const inbox = await inboxOf('*@dev.example.com')

const message = await waitForMessage(
  inbox,
  m => m.to.some(t => t.address === address),
)

const otp = message.text.match(/\b\d{6}\b/)?.[0]
const link = message.text.match(/https?:\/\/\S*confirm\S*/)?.[0]
```

For a long-running agent, skip polling: [subscribe over SSE](/docs/api#real-time-updates) and hold one connection instead of hammering the list endpoint.

## For the Agent Itself

The docs are agent-readable. `https://smtp.dev/llms.txt` indexes the site, and any docs page serves markdown when you append `.md` to its URL. The full API surface is in the [OpenAPI document](https://api.smtp.dev/docs.jsonld).

## Boundaries

Receiving is unrestricted: sandbox domains have real MX records, so any service the agent signs up for can deliver to them. Outbound is the opposite - mail from the sandbox only delivers to accounts inside it. An agent with sandbox credentials can read everything on your test domain and email nobody else, which is exactly the blast radius you want while it's under development.
