Skip to main content
Getting started File 006

Email for agents

What an AI agent can do with Postboi on its own. Set up sending with nobody signing in, give itself an inbox at tempboi.email or a mailbox it keeps at agentboi.email, sign up for things and read the code, and answer what arrives.

An agent that works with email needs four things: a way to send, an address to receive at, a way to read what arrives, and a way to test what it builds. Postboi covers all four, and none of them needs a person to sign in first.

The agent wants to… Use Needs an account?
Send email from the app it’s building bunx postboi init --agent, mail() No, a claimable sandbox is made for it
Sign up for something and read the code Tempboi No
Have an address of its own that stays Agent mailboxes No to make one; a person claims it before it sends
Reach all of this as MCP tools postboi mcp No: a mailbox key, or the project’s token for sending
Test a sign-up flow end to end temp() from postboi/inbox No
See what the app sent while developing The dev inbox No
Read replies to mail the app sent Receiving, /v1/inbound Yes, the project’s token

Send, with nobody signing in

bunx postboi init --agent

One command writes POSTBOI_TOKEN to .env, writes the config, installs the package and links the agent skill. With no token and no person to sign in, it makes a claimable project: sends run the whole pipeline (message log, webhooks, status) but are sandboxed, so nothing reaches a real inbox until somebody claims it. The CLI prints a claim URL. Put that URL in your summary for the person you’re working for, because it’s the one step only they can take. The details.

import { mail } from "postboi"

await mail({ to: "someone@example.com", subject: "Your code", body: "<p>4291</p>" })

Errors come back as one PostboiError with a code to branch on, whichever provider failed. postboi sync types from to the addresses the account can send from, so a wrong sender is a type error before it’s a failed send.

Receive, with Tempboi

Tempboi is Postboi’s throwaway inbox service. An agent can make an address at tempboi.email with no account, use it to sign up for something, and wait for the verification mail. The code and the link come back already pulled out:

eval "$(bunx postboi inbox new --env)"   # sets POSTBOI_INBOX and POSTBOI_INBOX_TOKEN
# sign up somewhere with $POSTBOI_INBOX
CODE=$(bunx postboi inbox wait --code --subject verify)

Or over plain HTTP, with nothing installed:

curl -X POST tempboi.email
curl -H "Authorization: Bearer tb_…" \
  "https://tempboi.email/v1/inboxes/<address>/wait?subject=verify"

The wait holds until a matching mail arrives and answers JSON with code, link, text, html and the SPF, DKIM and DMARC results. An inbox lives an hour unless asked for longer (24 hours at most). The whole API is at tempboi.email/llms.txt.

Keep an address, with agent mailboxes

When the agent needs an address that stays (a support desk, a booking assistant, anything people write back to), give it an agent mailbox at agentboi.email. It reads its mail as JSON, every message says whether it’s from your team or a stranger, and replies stay in the thread:

import { mailbox } from "postboi/mailbox"

const box = await mailbox()
for await (const mail of box.watch()) {
	if (mail.trust === "suspect") continue
	await box.reply(mail, { text: "On it." })
}

With no account, curl -X POST agentboi.email makes one that receives at once and sends once a person claims it. Put its claim link in your summary for them.

A team can ask for a person to look before the mailbox sends (review: "new" or "all"): then a send comes back as a draft waiting for them, and the agent should say so rather than send it again. The mailbox also files threads in folders and labels, and can write a draft on purpose when the agent would rather a person checked it.

Over MCP

postboi mcp is an MCP server on stdio with the mailbox, Tempboi and sending as tools, for Claude Code, Cursor, Codex or any other client:

claude mcp add postboi -- npx postboi mcp

It reads the project’s .env, so the key postboi init wrote is all it needs.

Every tool result that carries somebody else’s words wraps them in an <email> block naming their trust, so a stranger’s text never reads as the user’s. The tools and the config for each client are on the mailbox page.

Test the flow it built

The same inbox is one function in a test, so an agent that builds a sign-up flow can prove it works:

import { temp } from 'postboi/inbox'

await using inbox = await temp({ ttl: '15m' })
// sign up with inbox.address
const mail = await inbox.wait({ subject: 'Verify' })
mail.code

While developing, the dev inbox catches everything the app sends, so nothing reaches a real person, and email testing checks an email for client support, clipping and deliverability before it goes out.

Read replies

Mail sent from Postboi can be answered. Replies to the sending address, and mail to any address on reply.<your domain>, are kept and can be read over the API or handed to your code as an email.received webhook:

curl https://api.postboi.app/v1/inbound -H "Authorization: Bearer $POSTBOI_TOKEN"

To answer one in its thread, send with its id as in_reply_to, and the Postboi provider writes the headers that keep it there:

await mail({ to: "bob@example.com", subject: "Re: Hello", body: "<p>Thanks</p>", in_reply_to: "in_…" })

Reading these docs

Every page is Markdown at https://docs.postboi.app/raw/<slug>, the whole site is one file at /llms-full.txt, and the package ships a skill (bunx postboi skill) with all of this in it.