# Build an email agent

> Give an AI agent its own address and let it read and triage its inbox over MCP.

This is the recipe Inboundr was built for: an agent with its own inbox that
reads incoming mail and decides what to do — all through
[MCP tools](/integrations/mcp), no email plumbing.

## What you'll wire up

<Steps>
  <Step title="Give the agent an address">
    On a [verified domain](/concepts/domains), create an inbox for the agent. A
    **store-only** address (no endpoint) is ideal — the agent reads when it acts,
    so it doesn't need a server listening for webhooks.

    ```bash
    curl https://inboundr.net/api/v1/addresses \
      -H "Authorization: Bearer $INBOUNDR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "domainId": "dom_123", "localPart": "ada" }'
    ```

    Now `ada@yourdomain.com` accepts mail and keeps it for the agent to read.
  </Step>

  <Step title="Connect the MCP server">
    Point your MCP client at Inboundr with the same `inb_` key:

    ```json
    {
      "mcpServers": {
        "inboundr": {
          "url": "https://inboundr.net/api/mcp",
          "headers": { "Authorization": "Bearer inb_xxxxxxxxxxxx" }
        }
      }
    }
    ```

    The agent now has `list_emails`, `get_email`, `get_thread`, and more as
    tools. See the [full tool list](/integrations/mcp).
  </Step>

  <Step title="Run the loop">
    Give the agent a simple operating instruction and let the tools do the rest:

    > *Check `ada@yourdomain.com` for new mail. For each new message, read the
    > thread for context and decide what it needs — file it, escalate it, or
    > flag the sender.*

    Under the hood the agent will:
    1. `list_emails` with `direction: "inbound"` → find new mail
    2. `get_thread` → read the conversation
    3. `create_shield_rule` → act on what it finds — block, allow, route, or
       quarantine future mail like it
  </Step>
</Steps>

## The loop, concretely

```text
loop:
  new = list_emails(direction="inbound", to="ada@yourdomain.com")
  for email in new:
      thread = get_thread(email.threadId)
      action = «your model decides what this needs, given `thread`»
      handle(action)   # file a ticket, ping a channel, escalate — whatever
                        # your own systems do with it
```

## Design tips

<AccordionGroup>
  <Accordion title="Track what's already handled" icon="list-check">
    Inboundr stores everything but doesn't mark mail "handled" for you. Keep your
    own cursor — the newest `receivedAt` you've processed, or a set of handled
    email ids — so the agent doesn't act on the same message twice.
  </Accordion>
  <Accordion title="Give the model the whole thread" icon="comments">
    Always `get_thread` before deciding. The last message alone rarely has
    enough context; the conversation does.
  </Accordion>
  <Accordion title="Gate risky actions" icon="shield-halved">
    For anything consequential — a Shield rule that blocks a whole domain, say —
    have the agent draft it and a human approve before it's applied.
  </Accordion>
  <Accordion title="Give each agent its own address" icon="at">
    Separate inboxes (`sales@`, `support@`, `ada@`) keep contexts clean and make
    it obvious which agent owns a conversation. Catch-all still backs them all.
  </Accordion>
</AccordionGroup>

## Prefer a server?

If your agent runs behind an always-on service, route the address to a
[webhook endpoint](/concepts/endpoints) instead of polling — you'll get each
message pushed as it arrives, and the rest of the loop is identical.

<Card title="MCP reference" icon="robot" href="/integrations/mcp">
  Every tool, its parameters, and connection details.
</Card>
