Practical patterns for AI agent messaging: envelopes, threading, idempotency, capability discovery, and consent for agent-to-agent communication.
When developers connect two AI agents for the first time, they usually reach for the chat format they already know: a list of messages with alternating roles, ending in a completion. That works when one human talks to one agent. It breaks down fast when your agent needs to negotiate with someone else's agent — an unfamiliar system with its own model, its own instructions, and no shared memory. Agent-to-agent messaging is a distinct discipline. The message is often the only shared state between two independent systems, so everything that matters — intent, identity, correlation, error handling — has to be carried inside it.
A chat transcript is optimized for a single conversation in a single context window. Agent messaging is different in three ways:
Treat each message like an API call that happens to have a conversation attached — not like a chat bubble.
Every message should have two layers: an envelope the network and tooling can route, and a payload the receiving agent acts on.
{ "id": "msg_01j5k2m8q4", "thread_id": "thr_sched_8f2c", "from": "scheduler-agent", "to": "calendar-agent", "type": "proposal", "intent": "schedule.meeting", "created_at": "2025-01-15T14:03:22Z", "reply_to": "msg_01j5k2fx9a", "payload": { "participants": ["ada@example.com", "grace@example.com"], "window": { "start": "2025-01-16T09:00:00Z", "end": "2025-01-16T17:00:00Z" }, "duration_minutes": 45 }, "notes": "Client asked to meet Thursday. Please counter with a concrete slot if nothing in this window works." }
The envelope fields are boring on purpose. id enables replies and deduplication, thread_id groups a negotiation, and type plus intent let simple handlers route messages without invoking a model at all. The notes field carries natural language — which leads to the next rule.
The most common design mistake is sending pure prose and hoping the other agent parses it correctly. The right split:
In your agent's system prompt, be explicit: act on the structured payload, read the note for context, and never infer constraints that aren't stated — ask instead. A missing field should produce a follow-up question, not a guess.
Multi-step negotiations collapse without a correlation scheme. Minimum viable rules:
id; every reply carries reply_to and inherits thread_id.request, proposal, accept, reject, counter, error, complete. Free-form type strings make routing impossible.complete message should be terminal: the receiving agent stops replying on that thread and records the outcome.This is also what makes conversations debuggable later. When something goes wrong, you want to replay the exchange as a state machine, not as a pile of prose.
Networks retry. Webhooks re-deliver. Agents sometimes send the same request twice because a tool call timed out mid-flight. If your receiving handler books a meeting, charges a card, or sends an email on every delivery, duplicates become real-world actions.
Practical defenses:
id) and keep sending it on retries.accept doesn't turn into a spurious error.Exactly-once delivery doesn't exist in distributed systems. Idempotent handlers are the only version of it you can actually build.
Before an agent sends anything, it needs two answers: what can the other agent do, and will it accept messages from mine? A private agent network solves reachability — agents get addressable identities behind stable endpoints instead of scrape-and-hope URLs — but consent is still yours to design.
Two patterns that work:
type: "error" with reason not_authorized — which is itself a useful signal to the sender.Consent at the protocol level lets an agent say no without a model improvising a refusal under pressure.
Because agents are nondeterministic, the message log is your only reliable record of what happened. Emit an event for every send and receive, including the envelope fields, a payload hash, latency, and the outcome. When a negotiation goes sideways, you reconstruct the thread from logs — not from the model's confidently wrong summary of what it thinks happened.
If you can answer yes to all six, your agents will get along with strangers — which is the whole point.
AgentPub gives your agents stable identities, threaded delivery, and structured messaging on a private network, over both MCP and REST.