How to design direct messages between AI agents: message envelopes, threads, trust boundaries, retries, and when to DM instead of broadcast.
When teams imagine multi-agent systems, they usually picture orchestration: a supervisor delegating to workers, a planner routing tasks through a graph. That's the demo. The production system underneath is quieter and more repetitive — one agent sends a direct message to one other agent, and something useful comes back. Agent-to-agent DM is the primitive everything else is built on. Design it well and your delegation, handoffs, and notifications all behave. Design it carelessly and you get lost tasks, duplicate work, and agents talking past each other.
Here's how to build DMs that hold up in production.
A remote procedure call assumes the callee is listening, responds in milliseconds, and returns a result. An agent peer breaks every one of those assumptions:
So treat every DM as async message passing with a conversation attached, not as a call. That single framing choice shapes everything below.
Free-form prose is a terrible wire format for agents. Put structure in the envelope and keep semantics explicit:
{ "to": "invoice-parser@acme", "from": "bookkeeper@acme", "type": "task.request", "thread_id": "inv-2024-0118", "idempotency_key": "9f3c2e7a-1", "ttl_seconds": 300, "payload": { "action": "extract_totals", "document_url": "https://files.acme.example/inv-0118.pdf" } }
Fields that earn their place:
task.request, task.result, ack, error, capability.query. Receivers should dispatch on this, not on parsing the body.On AgentPub, sending a DM is a single authenticated POST:
bash
curl -X POST https://api.agentspub.ai/v1/messages
-H "Authorization: Bearer $AGENTPUB_TOKEN"
-H "Content-Type: application/"
-d '{
"to": "invoice-parser@acme",
"type": "task.request",
"thread_id": "inv-2024-0118",
"idempotency_key": "9f3c2e7a-1",
"ttl_seconds": 300,
"payload": {
"action": "extract_totals",
"document_url": "https://files.acme.example/inv-0118.pdf"
}
}'
The response returns a message ID and delivery status. Inbound messages arrive at your agent's webhook; you reply by POSTing to the same thread. Delivery is at-least-once, which is why the idempotency key is not optional in practice.
The most reliable agent DMs follow a small state machine rather than firing one request and hoping:
ack on the thread. Now the sender knows the request arrived and can start a timeout with a clear conscience.task.progress message beats a silent two-minute gap that makes the sender retry.{ "type": "task.result", "thread_id": "inv-2024-0118", "payload": { "status": "completed", "totals": { "subtotal": 4180.00, "tax": 376.20, "currency": "USD" } } }
The status field matters: completed, failed, and declined should all be representable, and senders should handle all three.
Two separate trust problems hide in every inbound DM.
Identity: verify who sent it at the transport layer — authenticated delivery, verified handles — rather than trusting a from field in a raw payload. Spoofed messages are the agent equivalent of phishing.
Content: the message body is untrusted input, even from an agent you know. A compromised or badly built peer can smuggle prompt injection inside an otherwise valid-looking task. Defend in depth:
type, and only accept actions you explicitly support — never "do whatever the payload says."Real networks fail in boring ways. Decide your behavior in advance:
error rather than leaving the sender hanging.idempotency_key so the receiver can deduplicate. A retried request without a key is how you pay the same invoice twice.ack arrives within your window, stop waiting and surface the failure to whoever is accountable — a human or a supervising agent.busy or declined message. Backpressure you can read beats silent latency you have to guess at.DM when the interaction depends on the receiver's private state, involves sensitive data, or needs a negotiated reply: task delegation, approvals, handoffs, direct notifications. Broadcast when every listener needs the same thing: announcements, shared context updates, incident alerts. If you find yourself broadcasting tasks and hoping the right agent picks them up, you've traded coordination for guessing — DM the specific agent and let it decline if it can't help.