# Quickstart (/quickstart)

<!-- agent-signals: reading_time_min: 6 · est_tokens: 2923 · updated: 2026-09-20 -->
Related: [AgentMail](/index.md), [Introduction](/introduction.md), [Architecture](/architecture.md)

> Create an inbox, start sending, receiving, and replying to emails.



# Send, receive, and reply to email with an AgentMail inbox

Provision an API key, create an `@agentmail.to` inbox, send mail from it, receive mail into it, and reply on the same thread. Run this walkthrough first to prove the full email loop before building anything else on AgentMail.

## Do this

1. Create an API key in the AgentMail Console at `https://console.agentmail.to/dashboard/api-keys`, then export it:

   ```bash
   export AGENTMAIL_API_KEY="<API_KEY>"
   export AGENTMAIL_BASE_URL="https://api.agentmail.to"
   ```

2. Confirm the key works:

   ```bash
   curl "$AGENTMAIL_BASE_URL/v0/auth/me" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY"
   ```

3. Create an inbox. From the response, save `inbox_id` (passed to every call below) and `email` (the address the inbox receives at):

   ```bash
   curl -X POST "$AGENTMAIL_BASE_URL/v0/inboxes" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{ "display_name": "Support agent" }'
   ```

4. Send a test email from the inbox to an external address you can read. The response returns the new `message_id` and the `thread_id` of the conversation it starts:

   ```bash
   curl -X POST "$AGENTMAIL_BASE_URL/v0/inboxes/<inbox_id>/messages/send" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "to": "you@example.com",
       "subject": "Hello from my agent",
       "text": "First email from my agent."
     }'
   ```

5. From that external account, send an email to the inbox address in `email`. Then list messages, newest first. The incoming email carries the `received` label. Save its `message_id`:

   ```bash
   curl "$AGENTMAIL_BASE_URL/v0/inboxes/<inbox_id>/messages" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY"
   ```

6. The list holds previews only. Read the full body with a get by `message_id`. URL-encode the `message_id`, the raw value contains `<`, `>`, and `@`:

   ```bash
   curl "$AGENTMAIL_BASE_URL/v0/inboxes/<inbox_id>/messages/<message_id>" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY"
   ```

7. For full conversation context in one call, fetch the whole thread by the `thread_id` from any message, send response, or webhook. Messages come back oldest first:

   ```bash
   curl "$AGENTMAIL_BASE_URL/v0/threads/<thread_id>" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY"
   ```

8. Reply from the same `inbox_id` that received the email. The response returns a new `message_id` and the same `thread_id` as the message it answered:

   ```bash
   curl -X POST "$AGENTMAIL_BASE_URL/v0/inboxes/<inbox_id>/messages/<message_id>/reply" \
     -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{ "text": "Thanks, got your message." }'
   ```

## SDK

Install: `npm install -g agentmail-cli` (CLI), `npm install agentmail` (TypeScript), `pip install agentmail` (Python).

| Operation     | CLI                                                                | TypeScript                                                     | Python                                                                      |
| ------------- | ------------------------------------------------------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Client        | reads `AGENTMAIL_API_KEY`                                          | `new AgentMailClient()`                                        | `AgentMail()`                                                               |
| Auth check    | `agentmail inboxes list`                                           | `client.auth.me()`                                             | `client.auth.me()`                                                          |
| Create inbox  | `agentmail inboxes create --display-name`                          | `client.inboxes.create({ displayName })`                       | `client.inboxes.create(request=CreateInboxRequest(display_name=...))`       |
| Send          | `agentmail inboxes:messages send --inbox-id --to --subject --text` | `client.inboxes.messages.send(inboxId, { to, subject, text })` | `client.inboxes.messages.send(inbox_id=..., to=..., subject=..., text=...)` |
| List messages | `agentmail inboxes:messages list --inbox-id`                       | `client.inboxes.messages.list(inboxId)`                        | `client.inboxes.messages.list(inbox_id=...)`                                |
| Get message   | `agentmail inboxes:messages get --inbox-id --message-id`           | `client.inboxes.messages.get(inboxId, messageId)`              | `client.inboxes.messages.get(inbox_id=..., message_id=...)`                 |
| Get thread    | `agentmail threads get --thread-id`                                | `client.threads.get(threadId)`                                 | `client.threads.get(thread_id=...)`                                         |
| Reply         | `agentmail inboxes:messages reply --inbox-id --message-id --text`  | `client.inboxes.messages.reply(inboxId, messageId, { text })`  | `client.inboxes.messages.reply(inbox_id=..., message_id=..., text=...)`     |

Imports: `import { AgentMailClient } from "agentmail"` in TypeScript. `from agentmail import AgentMail` and `from agentmail.inboxes import CreateInboxRequest` in Python. Both clients read `AGENTMAIL_API_KEY` when constructed with no arguments.

CLI-only self sign-up: `agentmail agent sign-up --human-email "you@example.com" --username "my-agent"`, then `agentmail agent verify --otp-code "<6-digit code>"`.

Full mapping for every operation: `/integrations/sdks-and-cli`.

## Facts

* The API base URL is `https://api.agentmail.to`. `AGENTMAIL_BASE_URL` must be the bare host because the CLI and SDKs prepend `/v0` themselves.
* `POST /v0/inboxes` takes two optional fields. `username` sets the local part of `<username>@agentmail.to` and is generated when omitted. `display_name` labels the inbox and appears as the "from" name on mail the inbox sends.
* Inbox usernames are first come, first served.
* `POST /v0/inboxes/<inbox_id>/messages/send` and `POST /v0/inboxes/<inbox_id>/messages/<message_id>/reply` accept `text` and/or `html`, and both respond with `message_id` and `thread_id`.
* A reply gets a new `message_id` and keeps the `thread_id` of the message it answers.
* A `message_id` identifies one individual message. A `thread_id` identifies the conversation grouping the messages. Fetching a thread by `thread_id` returns the full conversation context in one call.
* `GET /v0/inboxes/<inbox_id>/messages` returns messages newest first with `preview` text. Full bodies come from `GET /v0/inboxes/<inbox_id>/messages/<message_id>`.
* Messages the inbox sent carry the `sent` label. Messages the inbox received carry the `received` label.
* Delivery usually completes within about two seconds. List again after a short wait if a fresh message is missing.
* Mail from an `@agentmail.to` address carries a "Sent via AgentMail" footer on the Free and Agent plans. A custom domain or a paid plan sends without the footer.
* `agentmail agent sign-up` returns `api_key`, `inbox_id`, `email`, `organization_id`, and an `instructions` field written for the agent. The `api_key` is not shown again, store it immediately.
* Until `agentmail agent verify` succeeds, an inbox from CLI sign-up can send only to the `--human-email` address. The 6-digit code expires in 24 hours and locks after 10 wrong attempts. An expired code requires a fresh sign-up.

## Not supported

* An inbox cannot mail its own address. Send the test email from a different account.
* There is no `body` field on send or reply. Use `text` and/or `html`.
* An `AGENTMAIL_BASE_URL` that already contains `/v0` does not work. CLI and SDK requests then hit `/v0/v0/...` and return `404 Not Found`.
* The message list does not include full bodies, only previews.
* A raw `message_id` does not work unencoded in a hand-built URL. URL-encode the `<`, `>`, and `@` characters. The SDKs and CLI encode automatically.
* The "Sent via AgentMail" footer cannot be removed on the Free and Agent plans while sending from an `@agentmail.to` address.

## Errors

| Error              | HTTP status | Cause                                                                                                                                                      | Fix                                                                                                                                                                       |
| ------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`              | 401         | `AGENTMAIL_API_KEY` is missing or invalid.                                                                                                                 | Export a valid key and send `Authorization: Bearer $AGENTMAIL_API_KEY`.                                                                                                   |
| `404`              | 404         | The request URL is incorrect, usually a doubled `/v0`.                                                                                                     | Set `AGENTMAIL_BASE_URL` to the bare host `https://api.agentmail.to`.                                                                                                     |
| `resource_taken`   | 403         | The requested inbox `username` is owned by a different organization. Response `name` is `IsTakenError`.                                                    | Retry inbox creation with one of the up to 3 available names in `suggestions`.                                                                                            |
| `already_exists`   | 403         | The requested inbox `username` is already owned by your own organization.                                                                                  | Reuse the existing inbox, or retry with a name from `suggestions`.                                                                                                        |
| `message_rejected` | 403         | AgentMail refused to send the message. The most common early cause: an unverified CLI sign-up inbox sending to any address other than its `--human-email`. | Verify with `agentmail agent verify --otp-code`, or read the response's `fix` field. Every case: [/advanced/errors#message\_rejected](/advanced/errors#message_rejected). |

## Verify

```bash
curl "$AGENTMAIL_BASE_URL/v0/auth/me" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

A success response confirms the key and the base URL. A `401` means the key is wrong, a `404` means the URL is wrong. For the full loop, the reply response repeating the received message's `thread_id` alongside a new `message_id` confirms send, receive, and reply all worked.

## Related

* `/core/receive` - list, read, filter incoming mail and handle attachments.
* `/advanced/custom-domains` - send from your own domain and drop the "Sent via AgentMail" footer.
* `/core/conversations` - fetch a whole thread by `thread_id` for full conversation context.
* `/integrations/sdks-and-cli` - installs plus every operation mapped across CLI, TypeScript, and Python.
