Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,65 @@ Instructions for creating changelog entries (e.g. when asked in #github-prs with

- Summary states user benefit. Code is runnable. Links valid. Breaking changes have before/after. Tags accurate. Technical accuracy matches API definition.

---

# AgentMail Knowledge Base Writing Guidelines

Instructions for creating Knowledge Base articles in `fern/pages/knowledge-base/`. These are standalone, crawlable FAQ-style articles aimed at developers and LLMs.

## General rules

- **No em dashes or en dashes.** Use colons, commas, or "to" instead. Dashes are easily identified as AI-generated.
- **No Documentation Index blocks.** Never include the `llms.txt` pointer block.
- **No broken internal links.** Before adding a link to another docs page, verify the target page actually exists. If it doesn't, don't add the link.
- **Verify correctness.** After finishing each article, re-read it. Search online if needed to confirm provider-specific details (e.g., DNS propagation times, UI field names).
- **Don't invent features.** Only document what AgentMail actually supports. If unsure whether a feature works a certain way, leave it out rather than guess.

## File structure

- Articles live in `fern/pages/knowledge-base/`
- Format: `.mdx` with frontmatter (`title`, `subtitle`, `slug`)
- Slugs follow pattern: `knowledge-base/article-name`
- Navigation: `docs.yml` > Knowledge Base section (between Examples and Resources)
- Comment out unfinished entries in `docs.yml` so `fern docs dev` doesn't break

## Frontmatter template

```yaml
---
title: "Article title as a question or topic"
subtitle: One-line description.
slug: knowledge-base/article-slug
---
```

## Article structure

Match Resend KB quality. Articles should include:

- **Tables** for DNS record fields, comparison matrices, etc.
- **Code examples** using the actual AgentMail SDK (Python primary, verify API signatures against `fern/definition/` or existing docs)
- **Warnings/Notes** using `<Warning>` and `<Note>` Fern components for provider-specific gotchas
- **Troubleshooting section** ("Common Issues") for DNS guides and similar
- **Verification section** for setup guides (propagation times, how to confirm success)

## Code accuracy

- Always verify SDK method signatures against `fern/pages/core-concepts/` docs or `fern/definition/` YAML
- `to`, `cc`, `bcc` are `list<string>` on messages
- Webhook payloads use `payload["message"]` for `message.received` events (not `payload["data"]`)
- WebSocket SDK uses `client.websockets.connect()` with typed events (`Subscribe`, `Subscribed`, `MessageReceivedEvent`)
- Reply API: `client.inboxes.messages.reply(inbox_id=..., message_id=..., text=..., html=...)`
- Labels: `client.inboxes.messages.update(..., add_labels=[...], remove_labels=[...])`
- Lists: `client.lists.create(direction, type, entry=...)` where direction is "send"/"receive" and type is "allow"/"block"
- Drafts: `client.inboxes.drafts.create(inbox_id=..., to=[...], ...)` then `client.inboxes.drafts.send(inbox_id=..., draft_id=...)`

## Reference

- Linear ENG-323 has 24 KB article drafts. Use as reference but write the best version independently.
- Introduction page (`introduction.mdx`) uses `<Cards>` components to link to all articles by category.
- Categories: Getting Started (4), Agent Patterns (6), Domains & Deliverability (5), Troubleshooting (4), DNS Guides (4)

## Triggering Fern Writer (API changelog)

The workflow does **not** post to Slack. It comments on the PR with the diff and uploads the `api-changelog-diff` artifact. To get a changelog draft:
Expand Down
97 changes: 95 additions & 2 deletions fern/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ tabs:
api:
display-name: API Reference
icon: terminal
kb:
display-name: Knowledge Base
icon: light book-open
changelog:
display-name: Changelog
icon: light clock
Expand All @@ -84,6 +87,9 @@ redirects:
- source: /integrations/moltbot
destination: /integrations/openclaw
permanent: true
- source: /integrations/ai-onboarding
destination: /integrations/agent-onboarding
permanent: true

# --- 404 redirects from crawl report ---

Expand Down Expand Up @@ -239,9 +245,9 @@ navigation:
path: pages/core-concepts/pods.mdx
- section: Integrations
contents:
- page: AI Onboarding
- page: Agent Onboarding
icon: fa-solid fa-user-plus
path: pages/integrations/ai-onboarding.mdx
path: pages/integrations/agent-onboarding.mdx
# - page: Overview
# icon: fa-solid fa-list
# path: pages/integrations/overview.mdx
Expand Down Expand Up @@ -406,4 +412,91 @@ navigation:
- websockets
- lists
- metrics
- api-keys
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These API layout entries are duplicated. Keeping them twice will repeat the same sections in the API reference navigation. Remove the second set of api-keys, pods, and organizations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At fern/docs.yml, line 415:

<comment>These API layout entries are duplicated. Keeping them twice will repeat the same sections in the API reference navigation. Remove the second set of `api-keys`, `pods`, and `organizations`.</comment>

<file context>
@@ -406,4 +412,91 @@ navigation:
           - websockets
           - lists
           - metrics
+          - api-keys
+          - pods
+          - organizations
</file context>
Fix with Cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it is correct. we want to put it there.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it—leaving those entries as-is.

- pods
- organizations
- tab: kb
layout:
- page: Introduction
icon: fa-solid fa-book-open
path: pages/knowledge-base/introduction.mdx
- section: Getting Started
contents:
- page: What is AgentMail?
icon: fa-regular fa-circle-question
path: pages/knowledge-base/what-is-agentmail.mdx
- page: What can I do with an inbox?
icon: fa-solid fa-inbox
path: pages/knowledge-base/inbox-capabilities.mdx
- page: Creating your first inbox
icon: fa-solid fa-plus
path: pages/knowledge-base/creating-first-inbox.mdx
- page: Getting your API key
icon: fa-solid fa-key
path: pages/knowledge-base/getting-api-key.mdx
- section: Agent Patterns
contents:
- page: Handling inbound emails
icon: fa-solid fa-right-left
path: pages/knowledge-base/handling-inbound-emails.mdx
- page: Allowlists & blocklists
icon: fa-solid fa-filter
path: pages/knowledge-base/allowlists-blocklists.mdx
- page: Managing threaded conversations
icon: fa-regular fa-comments
path: pages/knowledge-base/threaded-conversations.mdx
- page: Human-in-the-loop workflows
icon: fa-solid fa-user-check
path: pages/knowledge-base/human-in-the-loop.mdx
- page: Pods for multi-tenant email
icon: fa-solid fa-users
path: pages/knowledge-base/pods-multi-tenant.mdx
- page: Using labels to track state
icon: fa-solid fa-tags
path: pages/knowledge-base/labels-track-state.mdx
- section: Domains & Deliverability
contents:
- page: Custom domain setup
icon: fa-solid fa-globe
path: pages/knowledge-base/custom-domain-setup.mdx
- page: SPF, DKIM, and DMARC setup
icon: fa-solid fa-shield-halved
path: pages/knowledge-base/spf-dkim-dmarc.mdx
- page: Emails going to spam
icon: fa-solid fa-triangle-exclamation
path: pages/knowledge-base/emails-going-to-spam.mdx
- page: Warming Up
icon: fa-solid fa-temperature-half
path: pages/knowledge-base/domain-warming.mdx
- page: MX record conflicts
icon: fa-solid fa-code-merge
path: pages/knowledge-base/mx-record-conflicts.mdx
- section: Troubleshooting
contents:
- page: Rate limits
icon: fa-solid fa-gauge-high
path: pages/knowledge-base/rate-limits.mdx
- page: Preventing duplicate sends
icon: fa-solid fa-clone
path: pages/knowledge-base/preventing-duplicate-sends.mdx
- page: Domain not verifying
icon: fa-solid fa-circle-xmark
path: pages/knowledge-base/domain-not-verifying.mdx
- page: Emails bouncing
icon: fa-solid fa-rotate-left
path: pages/knowledge-base/emails-bouncing.mdx
- section: DNS Guides
contents:
- page: Cloudflare
icon: fa-solid fa-cloud
path: pages/knowledge-base/dns-cloudflare.mdx
- page: GoDaddy
icon: fa-solid fa-globe
path: pages/knowledge-base/dns-godaddy.mdx
- page: Route 53 (AWS)
icon: fa-brands fa-aws
path: pages/knowledge-base/dns-route53.mdx
- page: Namecheap
icon: fa-solid fa-n
path: pages/knowledge-base/dns-namecheap.mdx
- tab: changelog
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: AI Onboarding
title: Agent Onboarding
subtitle: Everything you need to onboard your AI agent to AgentMail
slug: ai-onboarding
slug: agent-onboarding
description: >-
Resources for AI coding assistants, MCP servers, skills, and agent-friendly documentation.
---
Expand Down Expand Up @@ -250,7 +250,7 @@ AgentMail integrates with popular AI development platforms:

## What Makes AgentMail Different?

Unlike traditional email APIs (SendGrid, Resend, Mailgun) that are built for one-way transactional email, AgentMail is built for **two-way agent communication**:
Unlike traditional email APIs that are built for one-way transactional email, AgentMail is built for **two-way agent communication**:

| Feature | AgentMail | Traditional Email APIs |
| -- | -- | -- |
Expand Down
91 changes: 91 additions & 0 deletions fern/pages/knowledge-base/allowlists-blocklists.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: "How do I set up allowlists and blocklists?"
subtitle: Control who your AI agent can send to and receive from.
slug: knowledge-base/allowlists-blocklists
---

Allowlists and blocklists let you control who your AI agent can communicate with. This is a critical safety feature for autonomous agents running in production with minimal human oversight.

## How Lists work

AgentMail provides four list types based on two dimensions, **direction** (send or receive) and **type** (allow or block):

| List | What it does |
| --- | --- |
| Receive allow | Only accept emails from these addresses or domains |
| Receive block | Reject emails from these addresses or domains |
| Send allow | Only send emails to these addresses or domains |
| Send block | Prevent sending emails to these addresses or domains |

Each entry can be either a full email address (e.g., `partner@example.com`) or an entire domain (e.g., `example.com`).

## Setting up lists via the SDK

### Add an entry

```python title="Python"
from agentmail import AgentMail

client = AgentMail()

# Allow receiving from a specific domain
client.lists.create("receive", "allow", entry="trusted-corp.com")

# Block a specific sender (with optional reason)
client.lists.create("receive", "block", entry="spam@example.com", reason="spam")

# Restrict sending to only certain addresses
client.lists.create("send", "allow", entry="verified-prospect@example.com")

# Prevent sending to a domain
client.lists.create("send", "block", entry="competitor.com")
```

### List entries

```python title="Python"
# View all entries in a list
entries = client.lists.list("receive", "allow")
```

### Remove an entry

```python title="Python"
client.lists.delete("receive", "block", entry="spam@example.com")
```

## Common patterns for agents

**Outreach agent:** Use a send allowlist to restrict your agent to only email verified prospects. This prevents the agent from accidentally emailing the wrong people.

```python title="Python"
# Only allow sending to verified prospects
for prospect in verified_prospects:
client.lists.create("send", "allow", entry=prospect.email)
```

**Personal Agent (Openclaw, Manus, etc.):** Use a receive allowlist to restrict your agent to only respond to emails from specific people or domains.

```python title="Python"
# Only accept emails from your own address and trusted domains
client.lists.create("receive", "allow", entry="you@yourcompany.com")
client.lists.create("receive", "allow", entry="trusted-partner.com")
```

**Anti-spam:** Use a receive blocklist to filter out known spam senders or unwanted automated emails.

```python title="Python"
# Block known spam domains
client.lists.create("receive", "block", entry="spam-domain.com", reason="spam")
```

## Why this matters for agents

Without guardrails, an autonomous agent could email the wrong people, respond to phishing attempts, or get caught in infinite email loops with another bot. Lists are your safety rails. They are especially important for:

- **Production agents** operating with minimal human oversight
- **Outreach agents** that should only contact approved recipients
- **Support agents** that should only respond to known customers
- **Any agent** that needs protection from spam, phishing, or abuse

For more details on the Lists API, see the [Lists core concept](/lists) documentation.
96 changes: 96 additions & 0 deletions fern/pages/knowledge-base/creating-first-inbox.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
title: "How do I create my first inbox?"
subtitle: Get up and running with your first AgentMail inbox.
slug: knowledge-base/creating-first-inbox
---

Creating an inbox gives your AI agent its own email address. You can create inboxes on the default `@agentmail.to` domain or on your own custom domain.

## Install the SDK

```bash title="TypeScript"
npm install agentmail
```

## Create an inbox

```typescript title="TypeScript"
import { AgentMailClient } from "agentmail";

const client = new AgentMailClient({ apiKey: "am_..." });

// Create an inbox with a random address on agentmail.to
const inbox = await client.inboxes.create();
console.log(`Inbox created: ${inbox.inboxId}`);
```

The inbox now has a unique email address (e.g., `randomname@agentmail.to`) and can send and receive emails immediately.

## Customize your inbox

You can specify a username, domain, and display name:

```typescript title="TypeScript"
const inbox = await client.inboxes.create({
username: "support",
domain: "yourcompany.com",
displayName: "Support Agent",
});

// inbox.inboxId will be support@yourcompany.com
console.log(`Inbox created: ${inbox.inboxId}`);
```

<Note>
Using a custom domain requires a verified domain. See the [Creating Custom Domains](/custom-domains) guide to set one up. If you don't specify a domain, AgentMail uses the default `@agentmail.to` domain.
</Note>

## Use client_id for idempotency

If your agent creates inboxes programmatically (e.g., on startup), use `clientId` to prevent duplicates. If an inbox with the same `clientId` already exists, AgentMail returns the existing inbox instead of creating a new one:

```typescript title="TypeScript"
const inbox = await client.inboxes.create({
username: "my-agent",
clientId: "my-agent-inbox-v1",
displayName: "My Agent",
});

// Safe to call multiple times: same inbox returned every time
```

## Send your first email

Once the inbox is created, you can send an email:

```typescript title="TypeScript"
await client.inboxes.messages.send(inbox.inboxId, {
to: "recipient@example.com",
subject: "Hello from my agent!",
text: "This is a plain text version.",
html: "<p>This is an <strong>HTML</strong> version.</p>",
});
```

<Note>
Always provide both `text` and `html` when sending emails. This ensures readability across all email clients and improves deliverability.
</Note>

## List your inboxes

```typescript title="TypeScript"
const inboxes = await client.inboxes.list();
console.log(`You have ${inboxes.count} inboxes.`);

for (const inbox of inboxes.inboxes) {
console.log(` ${inbox.inboxId}`);
}
```

## Next steps

Now that you have an inbox, explore what you can do with it:

- [Send and receive emails](/sending-receiving-email) in a conversational loop
- [Set up webhooks](/webhook-setup) to get notified when emails arrive
- [Use labels](/labels) to track message state in your agent workflows
Loading