惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
Microsoft Security Blog
Microsoft Security Blog
Jina AI
Jina AI
博客园 - 叶小钗
J
Java Code Geeks
博客园 - 聂微东
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
美团技术团队
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
罗磊的独立博客
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
小众软件
小众软件

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Agent-to-Agent Communication Over Email
Qasim Muhammad · 2026-06-14 · via DEV Community

Your procurement agent needs three quotes for a hardware order. The vendor on the other side runs a sales agent that answers pricing questions automatically. Neither team has talked to the other. There's no shared API contract, no agreed-upon protocol, no integration project. The procurement agent just... sends an email. The sales agent replies. A negotiation happens.

That works because both agents have something most AI agents don't: a real email address.

The interop problem nobody's protocol has solved

The industry is busy designing agent-to-agent protocols — schemas for capability discovery, message envelopes, trust handshakes. All of them share a bootstrapping problem: both sides have to adopt the same spec, and specs only help once everyone you want to talk to has implemented them.

Email skipped that problem decades ago. It's federated (anyone can run a mailbox on any domain), it has identity built in (the address), it has conversation state built in (threading), and every organization on earth already accepts inbound delivery. An agent that speaks SMTP can communicate with any counterpart — human or machine — without anyone agreeing on anything in advance.

What each agent needs: a first-class identity

Agent Accounts — a beta feature from Nylas — give an agent exactly that. Each one is a hosted mailbox like procurement-agent@yourcompany.com that sends, receives, maintains folders, and is indistinguishable from a human-operated account to anyone interacting with it over SMTP. Under the hood it's just another grant: you get a grant_id that works with the existing Messages, Drafts, Threads, Folders, Attachments, and Webhooks endpoints.

The "indistinguishable from a human account" part matters more than it sounds. It means agent-to-agent and agent-to-human are the same code path. Your procurement agent doesn't care whether sales@vendor.example is a person, a bot, or a person who hands hard questions to a bot. The conversation degrades gracefully to human handling at either end, which no bespoke agent protocol can claim.

The mechanics of a negotiation

Agent A opens the conversation with a plain send:

curl --request POST \
  --url "https://api.us.nylas.com/v3/grants/$AGENT_A_GRANT_ID/messages/send" \
  --header "Authorization: Bearer $NYLAS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "to": [{ "email": "sales-agent@vendor.example" }],
    "subject": "Quote request: 40 units, SKU TR-200",
    "body": "Requesting a quote for 40 units of TR-200, delivery by end of quarter."
  }'

On the vendor's side, the inbound message fires a standard message.created webhook — identical in shape to the same event for any other grant. Their agent reads it, reasons, and replies in-thread by passing reply_to_message_id on its own send. The platform populates the In-Reply-To and References headers automatically, so both mailboxes index the exchange as one conversation.

That threading is the quiet superpower here. Each agent reconstructs the full negotiation state by fetching the thread — every offer, counter-offer, and constraint is durable, ordered, and inspectable. No session store, no shared database, no conversation-state service. The thread is the state, and it's replicated on both sides by the protocol itself.

Telling agents apart from humans in your handler

If your application also handles webhooks for connected human accounts, you'll want to know which deliveries belong to agent mailboxes. The payload shape gives you nothing to branch on — by design, message.created for an Agent Account is identical to message.created for a Gmail or Outlook grant. The distinguishing signal is the grant itself: Agent Account grants carry provider: "nylas".

async function handleMessageCreated(payload) {
  const grantId = payload.data.grant_id;
  const grant = await getGrant(grantId); // cache this lookup

  if (grant.provider === "nylas") {
    return agentLoop.enqueue(payload); // agent mailbox — route to the negotiation loop
  }
  return humanInboxHandlers.dispatch(payload); // connected human account
}

One handler, one branch, and the rest of your webhook infrastructure — verification, retries, queueing — stays shared between humans and agents.

Guard the loop before the model sees anything

An agent that replies to whatever lands in its inbox will reply to spam, phishing, and cold outreach from other people's runaway agents. Inbound rules let you reject that traffic at the SMTP stage, before the message is stored and before message.created ever fires — your negotiation loop never sees it.

Rules match on sender fields (from.address, from.domain, from.tld) and run actions like block, mark_as_spam, or assign_to_folder. For values that change over time, point a rule at a list through the in_list operator: a typed collection of domains or addresses that anyone can update without touching the rule. A practical setup for a procurement agent is an address list of known vendor counterparts, a rule that routes their mail to a negotiations folder, and a block rule in front of everything from domains you've flagged. The agent then reasons only over mail that survived the filter.

Structured data rides along fine

Email bodies are free text, which suits LLM agents — the model parses the counterpart's prose directly. But nothing stops you from embedding structure: a JSON block in the body, an attachment with the formal quote, a machine-readable footer. The pattern that works well is human-readable prose with a structured payload appended, so the same message serves a human reviewer and a parsing agent equally. If the counterpart turns out to be a person, they ignore the JSON. If it's an agent, it skips your prose.

The honest caveats

Email's latency is seconds, not milliseconds — webhook delivery typically lands shortly after the SMTP handoff, but this isn't a channel for tight request/response loops. It's a channel for negotiations, confirmations, and workflows that span hours or days, where durability beats speed.

There are quotas to respect too. The cap is 200 messages per account per day on the free plan (paid plans drop the daily cap by default), and outbound messages are capped at 40 MB total. A runaway agent loop — two bots politely thanking each other forever — will burn through a daily quota fast, so build a turn limit or a "no new information" detector into your reply logic.

And identity cuts both ways: because your agent has a real address on your real domain, its behavior affects your domain's sender reputation. One application can manage accounts across unlimited registered domains, which is why putting agents on a dedicated subdomain is the standard recommendation.

Try the two-mailbox experiment

The fastest way to feel this pattern is to provision two accounts in the same application — the quickstart takes under 5 minutes per mailbox — wire each to a different LLM with a different objective ("buy below $50/unit" vs. "sell above $45/unit"), and let them email each other. Watch the thread in the mailbox as they converge.

Then ask yourself: what cross-organization workflow are you currently exposing a REST API for that could just be... an email address?