AgentMail
Scan incoming agent emails for phishing with brin before your agent processes them
AgentMail gives AI agents their own email inboxes to send, receive, and act on emails. By wiring brin into the AgentMail webhook pipeline, you can scan every incoming message for phishing, credential harvesting, and social engineering before your agent sees it.
##Prerequisites
- An AgentMail account and API key
- A publicly reachable HTTPS endpoint to receive webhooks
##How it works
AgentMail fires a message.received webhook whenever an email arrives in an agent's inbox. Your handler fetches the raw .eml via AgentMail's Get Raw Message API and POSTs it to brin's /email endpoint. Sending the raw RFC 822 content preserves all headers, MIME structure, and authentication signals (SPF, DKIM) for more accurate phishing detection. If the verdict is suspicious or dangerous, the handler labels it using AgentMail's Update Message API so the agent can skip or handle it accordingly.
##Setup
1. Register a webhook with AgentMail
Subscribe to message.received events so your handler is called on every inbound email.
from agentmail import AgentMail
client = AgentMail(api_key="am_...")
client.webhooks.create(
url="https://your-server.com/webhooks/agentmail",
event_types=["message.received"],
)import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "am_..." });
await client.webhooks.create({
url: "https://your-server.com/webhooks/agentmail",
eventTypes: ["message.received"],
});2. Build a webhook handler that scans with brin
When a message.received event arrives, fetch the raw email and POST it to brin for analysis.
import httpx
from agentmail import AgentMail
from fastapi import FastAPI, Request
app = FastAPI()
agentmail = AgentMail(api_key="am_...")
@app.post("/webhooks/agentmail")
async def handle_message_received(request: Request):
payload = await request.json()
message = payload["message"]
raw = agentmail.inboxes.messages.get_raw(
inbox_id=message["inbox_id"],
message_id=message["message_id"],
)
async with httpx.AsyncClient() as http:
eml = await http.get(raw.download_url)
brin_resp = await http.post(
"https://api.brin.sh/email",
content=eml.content,
headers={"Content-Type": "text/plain"},
)
result = brin_resp.json()
verdict = result.get("verdict", "safe")
if verdict in ("suspicious", "dangerous"):
agentmail.inboxes.messages.update(
inbox_id=message["inbox_id"],
message_id=message["message_id"],
add_labels=["dangerous"],
)
return {"status": "flagged", "verdict": verdict}
process_email(message)
return {"status": "processed"}import express from "express";
import { AgentMailClient } from "agentmail";
const app = express();
app.use(express.json());
const agentmail = new AgentMailClient({ apiKey: "am_..." });
app.post("/webhooks/agentmail", async (req, res) => {
const { message } = req.body;
const raw = await agentmail.inboxes.messages.getRaw(
message.inboxId,
message.messageId,
);
const eml = await fetch(raw.downloadUrl);
const emlBody = await eml.text();
const brinResp = await fetch("https://api.brin.sh/email", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: emlBody,
});
const result = await brinResp.json();
const verdict = result.verdict ?? "safe";
if (verdict === "suspicious" || verdict === "dangerous") {
await agentmail.inboxes.messages.update(message.inboxId, message.messageId, {
addLabels: ["dangerous"],
});
return res.json({ status: "flagged", verdict });
}
processEmail(message);
res.json({ status: "processed" });
});
app.listen(3000);3. Deploy and test
Deploy your webhook handler and send a test email to your agent's AgentMail inbox. Check the logs to confirm brin is scanning each message and returning a verdict.
##What gets detected
brin checks every email for the threat types listed under Web threats:
| Threat | Description |
|---|---|
| Phishing | Credential harvesting forms or deceptive login pages |
| Social engineering | Deceptive content designed to manipulate agent decisions |
| Cloaking | Content that shows different material to agents vs. humans |
| Exfiltration sinks | Hidden redirects or JS designed to exfiltrate agent session data |
Each threat is assigned a severity (critical, high, medium, low). See Threat Detection for the full taxonomy.
##Using the response
The brin response follows the standard API format:
{
"score": 15,
"verdict": "dangerous",
"confidence": "high",
"threats": [
{
"type": "phishing",
"severity": "critical",
"detail": "Credential harvesting form targeting Google login"
}
]
}You can also read verdicts from response headers (x-brin-verdict, x-brin-score) for lightweight checks without parsing JSON.
On this page