Docs / Guides

Give your AI agent the ability to send email

Say you've built an agent. Maybe it's Claude in your terminal, maybe it's an app you wrote on top of an LLM. Sooner or later it needs to send a real email, a signup confirmation or a note that the nightly job finished. Handing it SMTP credentials feels wrong, and standing up an email service for one tool call is a lot of yak-shaving.

There are two clean ways to do this with PennyPost. Connect the hosted MCP server and the agent sends on its own, with no integration code from you. Or call the API from your agent's code when you're the one deciding to send. This covers both, then the single real step from test sends to live ones.

Pick the one that matches your setup

If your agent already uses tools, Claude Code, Cursor, anything that speaks the Model Context Protocol, connect the MCP server. The agent gets an email tool it can call and you write nothing. If you call the model yourself and decide when to send, skip ahead to calling the API. That path is one HTTP request.

Connect the MCP server

PennyPost runs a hosted MCP server. Point your client at it with a key and the agent can send email, read its own send log, and check domains. Each tool maps to one API endpoint, so an agent is doing exactly what a REST call would.

In Claude Code, from your terminal:

# a test key, so nothing leaves while you build. swap to pp_live_ to send for real
claude mcp add --transport http pennypost \
  https://api.pennypost.io/mcp \
  --header "Authorization: Bearer pp_test_..."

Now ask it to send one:

Send a test email to me@example.com, subject “Hello from my agent”, and a one-line body.

It comes back with a message id, and the send appears in your dashboard log marked as simulated. Cursor, Windsurf, and anything that reads an mcp.json take the same endpoint:

{
  "mcpServers": {
    "pennypost": {
      "url": "https://api.pennypost.io/mcp",
      "headers": { "Authorization": "Bearer pp_test_..." }
    }
  }
}

Or call the API from your own code

When your code decides to send and the model just produced the recipient, subject, and body, there's no SDK to learn. It's one request:

// your agent produced `to`, `subject`, and `body`
await fetch("https://api.pennypost.io/v1/emails", {
  method: "POST",
  headers: {
    "Authorization": "Bearer pp_test_...",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ from: "you@yourdomain.com", to, subject, text: body })
})

Same endpoint a person would hit, same key. If you prefer a typed client, the SDKs wrap this in six languages, but you don't need one to send.

Going from test to live

Everything above runs on a test key. It simulates delivery, so you can let an agent loop on it without a single real email going out or any effect on your sending reputation. To send for real, verify a domain you own: you add a few DNS records, PennyPost confirms them, and your live key starts working. The domain setup takes a few minutes.

Then put pp_live_ where the test key was, in the MCP command or in your code. Nothing else changes.

Why route agent email through here

An agent that can send email can also send a lot of it, including email you didn't mean to send. Every call, whether from the MCP tool or your own code, runs through the same domain checks, the same daily cap, and the same suppression list. A hard bounce or a complaint suppresses that address on its own, so a stuck loop can't quietly wreck your deliverability. And every send sits in the log next to the key that made it, so when something looks off you can see exactly what the agent did.

Start sendingThe free tier is 3,000 emails a month with no card. The full API reference has the rest.