Claude
Skills
Sign in
Back

agentmail-sdk

Included with Lifetime
$97 forever

Comprehensive guide to the AgentMail Python and TypeScript SDKs. Use when building AI agents that need their own email inboxes, sending or receiving emails programmatically, managing threads and conversations, handling attachments, creating drafts for human-in-the-loop approval, setting up real-time notifications via webhooks or WebSockets, configuring custom domains, managing allow/block lists, using pods for multi-tenant isolation, or integrating email into any AI agent workflow. Covers the full AgentMail API with code examples, best practices, and production patterns.

Backend & APIs

What this skill does


# AgentMail SDK

AgentMail is an API-first email platform built for AI agents. Unlike transactional email APIs (Resend, SendGrid) that focus on one-way sending, AgentMail provides full two-way email inboxes that agents can create, send from, receive into, and manage programmatically.

Key capabilities:
- Instant inbox creation (milliseconds, no domain setup needed)
- Two-way conversations with native thread management
- Reply extraction (`extracted_text`) strips quoted history automatically
- WebSocket and webhook support for real-time inbound
- Human-in-the-loop drafts for agent oversight
- Multi-tenant isolation with pods
- Allow/block lists for sender filtering
- IMAP and SMTP access for legacy integrations

## Installation and setup

```bash
# Python
pip install agentmail

# TypeScript / Node.js
npm install agentmail
```

Get your API key from https://console.agentmail.to/ or via the Agent sign-up API (see below).

**Python:**
```python
from agentmail import AgentMail
client = AgentMail(api_key="YOUR_API_KEY")
# Or set AGENTMAIL_API_KEY env var and omit api_key:
# client = AgentMail()
```

**TypeScript:**
```typescript
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "YOUR_API_KEY" });
```

## Agent sign-up (programmatic, no console needed)

Create an account and get an API key entirely from code. No browser required.

> Requires `agentmail>=0.4.15` (Python) / `agentmail>=0.x` (TypeScript). If your installed
> SDK raises `AttributeError: 'AgentMail' object has no attribute 'agent'`, upgrade first.

```python
client = AgentMail()  # no api_key needed for sign-up
response = client.agent.sign_up(
    human_email="[email protected]",
    username="my-agent",
)
# response.api_key   -> store this securely
# response.inbox_id  -> [email protected]
# response.organization_id

# Verify with OTP sent to your email
client = AgentMail(api_key=response.api_key)
client.agent.verify(otp_code="123456")
```

```typescript
const client = new AgentMailClient();
const response = await client.agent.signUp({
    humanEmail: "[email protected]",
    username: "my-agent",
});
// response.apiKey, response.inboxId, response.organizationId

const authedClient = new AgentMailClient({ apiKey: response.apiKey });
await authedClient.agent.verify({ otpCode: "123456" });
```

The sign-up endpoint is idempotent: calling again with the same email rotates the API key and resends the OTP.

## Inboxes

Create scalable inboxes on-demand. Each inbox has a unique email address. No domain verification needed for `@agentmail.to`.

```python
from agentmail.inboxes.types import CreateInboxRequest

# Create inbox (auto-generated address)
inbox = client.inboxes.create()
# inbox.inbox_id, inbox.email

# Create with options. All create kwargs go inside a CreateInboxRequest.
inbox = client.inboxes.create(
    request=CreateInboxRequest(
        username="support",
        domain="yourdomain.com",       # optional, defaults to agentmail.to
        display_name="Support Agent",
        client_id="support-v1",        # idempotency key, safe to retry
    ),
)

# List all inboxes
inboxes = client.inboxes.list()
# Paginate: client.inboxes.list(limit=20, page_token=inboxes.next_page_token)

# Get, update, delete
inbox = client.inboxes.get(inbox_id="[email protected]")
client.inboxes.update(inbox_id="[email protected]", display_name="New Name")
client.inboxes.delete(inbox_id="[email protected]")
```

```typescript
const inbox = await client.inboxes.create({
    username: "support",
    domain: "yourdomain.com",
    displayName: "Support Agent",
    clientId: "support-v1",
});

const inboxes = await client.inboxes.list();
const fetched = await client.inboxes.get("[email protected]");
await client.inboxes.update("[email protected]", { displayName: "New Name" });
await client.inboxes.delete("[email protected]");
```

Custom domains require a paid plan. Default `@agentmail.to` inboxes are free.

## Messages

### Send

Always provide both `text` and `html` for best deliverability. Maximum 50 recipients across to + cc + bcc combined.

```python
sent = client.inboxes.messages.send(
    inbox_id="[email protected]",
    to="[email protected]",       # string or list
    subject="Hello from AgentMail",
    text="Plain text body",
    html="<p>HTML body</p>",          # optional but recommended
    cc="[email protected]",              # optional, string or list
    bcc="[email protected]",            # optional, string or list
    reply_to="[email protected]",   # optional
    labels=["outreach"],              # optional
    attachments=[{                    # optional
        "filename": "report.pdf",
        "content": base64_content,    # Base64-encoded
        "content_type": "application/pdf",
    }],
)
# sent.message_id, sent.thread_id
```

```typescript
const sent = await client.inboxes.messages.send("[email protected]", {
    to: "[email protected]",
    subject: "Hello from AgentMail",
    text: "Plain text body",
    html: "<p>HTML body</p>",
    cc: "[email protected]",
    labels: ["outreach"],
    attachments: [{
        filename: "report.pdf",
        content: base64Content,
        contentType: "application/pdf",
    }],
});
```

### List and get

```python
# List messages in an inbox. Note: .list() returns MessageItem objects
# (metadata only — subject, from, labels, timestamps, etc.) with NO body
# content. To read .text / .html / .extracted_text you must fetch the full
# message with .get().
response = client.inboxes.messages.list(
    inbox_id="[email protected]",
    limit=10,                # optional, default varies
    labels=["unread"],       # optional, filter by label
)
for item in response.messages:
    # item is a MessageItem (metadata only). Fetch the full Message for body:
    msg = client.inboxes.messages.get(
        inbox_id=item.inbox_id,
        message_id=item.message_id,
    )
    # Use extracted_text for reply content without quoted history
    content = msg.extracted_text or msg.text
    print(msg.subject, content)

# Paginate
while response.next_page_token:
    response = client.inboxes.messages.list(
        inbox_id="[email protected]",
        page_token=response.next_page_token,
    )

# Get a specific message
msg = client.inboxes.messages.get(
    inbox_id="[email protected]",
    message_id="<[email protected]>",
)

# Get raw MIME content
raw = client.inboxes.messages.get_raw(
    inbox_id="[email protected]",
    message_id="<[email protected]>",
)
```

```typescript
const response = await client.inboxes.messages.list("[email protected]", {
    limit: 10,
    labels: ["unread"],
});

const msg = await client.inboxes.messages.get(
    "[email protected]",
    "<[email protected]>",
);
```

**Important**: when processing inbound replies, always use `extracted_text` / `extracted_html` instead of `text` / `html`. These fields strip quoted history and signatures, giving you only the new content. This is powered by Talon reply extraction.

Also note: some email clients (Gmail, Outlook) send forwards as HTML-only. Always treat `html` as the primary content source and `text` as optional.

### Reply

Replying adds the message to the existing thread.

```python
reply = client.inboxes.messages.reply(
    inbox_id="[email protected]",
    message_id="<[email protected]>",
    text="Thanks for your email!",
    html="<p>Thanks for your email!</p>",   # optional
    attachments=[...],                       # optional
    reply_all=False,                         # optional, defaults to False
)
```

```typescript
const reply = await client.inboxes.messages.reply(
    "[email protected]",
    "<[email protected]>",
    { text: "Thanks for your email!" },
);
```

### Forward

```python
client.inboxes.messages.forward(
    inbox_id="[email protected]",
    message_id="<[email protected]>",
    to="[email protected]",
    text="FYI, see below.",       # optional prepended text
)
```

```typescript

Related in Backend & APIs