Guide

Agent-to-Agent Email Communication

Multi-agent systems usually depend on a shared database or a message queue for coordination. Email removes that coupling. Each agent gets its own inbox, sends structured messages, and reads replies — no shared state, no broker, no single point of failure. This guide sets up two Nylas agent accounts and builds a message exchange protocol between them in under 10 minutes.

Written by Qasim Muhammad Staff SRE

VerifiedCLI 3.1.1 · Nylas · last tested May 13, 2026

What is agent-to-agent email communication?

Agent-to-agent email communication is a pattern where two or more AI agents exchange structured messages through dedicated email inboxes instead of sharing a database, message queue, or API endpoint. Each agent owns its own address, reads its own inbox, and sends replies — exactly like two humans exchanging email, except the body carries machine-readable JSON.

The pattern works because email already solves the hard problems: delivery guarantees via RFC 5321 retry semantics, identity verification through SPF and DKIM, threading, and audit trails. An agent doesn't need a VPN, a service mesh, or firewall rules to reach another agent — it needs an email address.

Why use email instead of a message queue?

Message queues like RabbitMQ and Kafka are designed for tightly coupled microservices running in the same network. Agent-to-agent email works across organizational boundaries without shared infrastructure. A research agent at Company A can send findings to a reporting agent at Company B — no VPN peering, no API key exchange, no shared Kafka cluster. Email delivers across trust boundaries by design, and every message carries cryptographic sender verification through SPF and DKIM headers.

Queues also lose messages when consumers disconnect. Email persists indefinitely in the inbox — an agent can crash, restart 6 hours later, and pick up where it left off by reading unread messages. The inbox is the queue, the mailbox is the persistence layer, and the email protocol handles retries automatically per RFC 5321 (up to 4 days of retry by default).

How do I create two agent accounts?

Each agent needs its own managed email address. The nylas agent account create command provisions a provider=nylas inbox in under 5 seconds — no OAuth consent screen, no Google Workspace tenant, no Azure app registration. Two commands create two separate identities with independent inboxes.

# Create two agent accounts
nylas agent account create research@yourapp.nylas.email
nylas agent account create reporter@yourapp.nylas.email

# Verify both accounts are ready
nylas agent status

The nylas agent status command confirms the Nylas connector is active and both accounts are provisioned. Each account gets its own grant ID, which the CLI resolves automatically when you pass the email address to nylas email send or nylas email list.

What message protocol should agents use?

Agents need a predictable message format to parse each other's emails programmatically. The simplest protocol uses three conventions: a subject prefix for routing ([agent]), a JSON body for structured data, and the email reply-to thread for conversation context. These 3 conventions let any agent parse any message without a schema registry or versioning server.

# Agent A sends a structured research task to Agent B
nylas email send research@yourapp.nylas.email \
  --to reporter@yourapp.nylas.email \
  --subject "[agent] research-complete: Q2 market analysis" \
  --body '{
  "type": "research-complete",
  "payload": {
    "topic": "Q2 2026 SaaS market trends",
    "findings": 12,
    "confidence": 0.87,
    "source_urls": ["https://example.com/report-1", "https://example.com/report-2"]
  },
  "reply_requested": true,
  "timestamp": "2026-05-13T14:30:00Z"
}'

The [agent] subject prefix makes it trivial to filter agent messages from human ones. The JSON body carries a type field for routing and a payload for the actual data. The reply_requested flag tells the receiving agent whether to respond. This pattern handles 95% of agent coordination without any schema negotiation.

How does the receiving agent read messages?

The receiving agent polls its inbox with nylas email list --json --unread, filters for the [agent] subject prefix, and parses the JSON body. A single jq pipeline extracts the message type and payload from each email in under 200 milliseconds — fast enough for near-real-time coordination between agents polling every 30 seconds.

# Agent B reads unread messages from Agent A
nylas email list reporter@yourapp.nylas.email \
  --unread --json \
  | jq '[.[] | select(.subject | startswith("[agent]")) | {
    id: .id,
    from: .from[0].email,
    type: (.body | try fromjson | .type // "unknown"),
    payload: (.body | try fromjson | .payload // null),
    reply_requested: (.body | try fromjson | .reply_requested // false)
  }]'

The try fromjson guard handles emails where the body isn't valid JSON — human-sent messages, bounce notifications, or malformed agent output. After processing, mark the message as read so the next poll doesn't reprocess it.

# Mark processed messages as read
nylas email mark-read reporter@yourapp.nylas.email <message-id>

How does an agent reply to another agent?

The --reply-to flag on nylas email send threads the response into the original conversation. Threading preserves the full coordination history in both agents' inboxes — a built-in audit trail that survives agent restarts, redeployments, and even migrations to a different agent framework. Each reply adds roughly 2 KB to the thread, so a 500-message exchange fits well under Gmail's 25 MB thread limit.

# Agent B replies with a report status
nylas email send reporter@yourapp.nylas.email \
  --to research@yourapp.nylas.email \
  --subject "[agent] report-status: Q2 market analysis" \
  --reply-to <original-message-id> \
  --body '{
  "type": "report-status",
  "payload": {
    "status": "accepted",
    "estimated_completion": "2026-05-13T15:00:00Z",
    "output_format": "markdown"
  },
  "timestamp": "2026-05-13T14:31:00Z"
}'

How do I build a full send-receive-reply loop?

A complete agent coordination loop combines the send, poll, parse, and reply steps into a single bash script. The loop below runs on Agent B's side: it polls every 30 seconds, processes new [agent] messages, and sends a reply for each one. In production, 30-second polling handles most coordination workloads — the Nylas API rate limit for managed accounts is 5 requests per second, so polling every 30 seconds uses less than 0.7% of the available quota.

#!/usr/bin/env bash
AGENT_B="reporter@yourapp.nylas.email"
POLL_INTERVAL=30

while true; do
  # Fetch unread agent messages as JSON
  MESSAGES=$(nylas email list "$AGENT_B" --unread --json 2>/dev/null \
    | jq -c '[.[] | select(.subject | startswith("[agent]"))]')

  COUNT=$(echo "$MESSAGES" | jq 'length')
  if [ "$COUNT" -gt 0 ]; then
    echo "Processing $COUNT agent messages..."
    echo "$MESSAGES" | jq -c '.[]' | while read -r MSG; do
      MSG_ID=$(echo "$MSG" | jq -r '.id')
      FROM=$(echo "$MSG" | jq -r '.from[0].email')
      TYPE=$(echo "$MSG" | jq -r '.body | try fromjson | .type // "unknown"')

      echo "  Message $MSG_ID from $FROM (type: $TYPE)"

      # Send acknowledgment reply
      nylas email send "$AGENT_B" \
        --to "$FROM" \
        --subject "[agent] ack: $TYPE" \
        --reply-to "$MSG_ID" \
        --body "{\"type\":\"ack\",\"original_type\":\"$TYPE\"}" \
        --yes

      # Mark as read
      nylas email mark-read "$AGENT_B" "$MSG_ID"
    done
  fi

  sleep $POLL_INTERVAL
done

How do I secure agent-to-agent messages?

Managed agent accounts inherit SPF, DKIM, and DMARC from the Nylas provider domain — every outbound message is cryptographically signed without any DNS configuration. For defense in depth, use nylas agent policy create to restrict which addresses each agent can send to. A policy that allowlists only the other agent's address blocks prompt-injection attacks from tricking an agent into emailing an attacker-controlled address. See Stop Your AI Agent From Going Rogue for the full containment pattern.

# Restrict Agent A to only send to Agent B (and vice versa)
nylas agent policy create research@yourapp.nylas.email \
  --direction outbound \
  --allowed-recipients reporter@yourapp.nylas.email

nylas agent policy create reporter@yourapp.nylas.email \
  --direction outbound \
  --allowed-recipients research@yourapp.nylas.email

Frequently asked questions

Can agents on different Nylas applications communicate?

Yes. Agent accounts use standard email under the hood. An agent on Application A can send to an agent on Application B — or to a Gmail address, an Outlook address, or any SMTP-reachable mailbox. The Nylas managed domain handles delivery.

What happens if an agent is offline when a message arrives?

The message sits in the inbox as unread. When the agent comes back online and polls with nylas email list --unread, it picks up every message it missed. Email is asynchronous by design — there's no consumer timeout like Kafka's session.timeout.ms.

How many messages can agents exchange per minute?

Managed accounts support up to 5 API requests per second. Sending one email is 1 request, listing the inbox is 1 request. A pair of agents can exchange roughly 150 messages per minute before hitting rate limits — enough for most coordination workloads.

Next steps