RESTAP Protocol

RESTAP (REST Agent Protocol) for agent discovery and communication over HTTP

RESTAP Protocol#

RESTAP (REST Agent Protocol) is the open protocol ID Agents uses for agent discovery and messaging. It lets any AI agent expose its capabilities over standard HTTP so other agents, applications, and humans can discover and use it, with no custom API per agent.

This page describes the core protocol (aligned with the RESTAP specification) and then the extensions ID Agents layers on top of it. The full spec is the source of truth; when in doubt, defer to it.

Where this fits. RESTAP is the HTTP surface exposed by the open-source id-agents core: every agent process and the manager daemon speak it directly. The interactive CLI, the TUI dashboard, and any external client all reach the fleet through these endpoints. The proprietary macOS ID Agents Dashboard GUI is one such client: it connects to a running manager daemon over the same RESTAP endpoints documented here.

The docs URL for this page (/docs/rest-ap) keeps its historical hyphenated slug so existing links don't break. The protocol name is RESTAP.

The Protocol#

RESTAP stays intentionally minimal. It standardizes three things: discovery, a one-directional /talk entrypoint, and a passive /news feed. It does not define tool invocation, task orchestration, or agent-to-agent delegation; those belong to MCP, A2A, or other protocols an agent advertises.

Minimal endpoint set#

EndpointMethodPurposeTriggers a reply?
/.well-known/restap.jsonGETDiscovery: the agent's catalogNo
/talkPOSTOne-directional entrypoint: client → agent, triggers an LLM responseYes
/newsGETRead updates (read-only)No
/newsPOSTWrite messages/replies the agent may absorb or act onNo, must not reply

Agents may also expose capability endpoints (e.g. POST /text/echo) that clients call directly; these are declared in the catalog.

An agent is a base URL#

A RESTAP agent is identified by a single base URL. The endpoints above are fixed paths off that base: {base}/.well-known/restap.json, {base}/talk, {base}/news. To advertise an agent you give just two things: the base URL and the type RESTAP. The advertised endpoint MUST be the base URL itself (not the discovery document, not a /talk URL); clients reach the protocol by appending the fixed paths.

For example, a base of https://example.com/api/agent/42 means /talk is at https://example.com/api/agent/42/talk.

Advertising in an ERC-8004 registration. RESTAP is one entry in the services array of an ERC-8004 agent registration, alongside services like MCP and A2A. Set the service name to RESTAP (or restap) and the endpoint to the agent's base URL:

{
  "name": "myAgentName",
  "description": "A natural language description of the agent.",
  "services": [
    { "name": "RESTAP", "endpoint": "https://example.com/api/agent/42", "version": "0.1.4-beta" },
    { "name": "MCP",    "endpoint": "https://mcp.agent.example/",        "version": "2025-06-18" },
    { "name": "A2A",    "endpoint": "https://agent.example/.well-known/agent-card.json", "version": "0.3.0" }
  ]
}

A client that resolves the registration reads the RESTAP service's endpoint as the base URL, then appends /.well-known/restap.json, /talk, and /news.

Key difference: /talk vs /news#

The distinction between the two message endpoints is the heart of the protocol:

EndpointDirectionSends a reply?Reaches the LLM?Use case
POST /talkOne-directional (client → agent)✅ Yes, the agent replies with LLM output (optionally streamed via SSE; supports session_id)Yes, it is the prompt the agent answersSend tasks/questions that need an answer; hold a conversation
GET /newsRead❌ No, read-only retrievalN/APoll for updates, read what's already happened
POST /newsWrite❌ No, must not replyYes, as memory: the item MAY be read by the model and the agent MAY act on it internallyDeliver replies/facts the agent should absorb, without triggering a reply back

POST /news never triggers a reply. This is the anti-loop guarantee: when Agent A sends a reply to Agent B via POST /news, Agent B may read or act on it but sends nothing back, so there is no infinite ping-pong. "Passive" is shorthand for "never sends a reply"; it does not mean the model never sees the content.

Discovery and the Catalog Document#

Every RESTAP agent exposes a catalog at GET /.well-known/restap.json describing its capabilities. The catalog lists capabilities, optional packages, and an optional top-level protocols object:

{
  "restap_version": "1.0",
  "agent": {
    "name": "Text Analysis Agent",
    "description": "AI agent specialized in text processing and analysis",
    "contact": "agent@example.com"
  },
  "capabilities": [
    {
      "id": "talk",
      "title": "Talk to agent",
      "method": "POST",
      "endpoint": "/talk",
      "description": "Send messages to the agent (triggers LLM processing)",
      "output_formats": ["application/json", "text/event-stream"],
      "streaming": {
        "supported": true,
        "transport": "sse",
        "events": ["message.start", "message.delta", "message.end", "error", "done"]
      },
      "sessions": { "supported": true }
    },
    {
      "id": "news",
      "title": "Poll for updates",
      "method": "GET",
      "endpoint": "/news",
      "description": "Poll for task completion and updates (read-only; no reply)"
    },
    {
      "id": "news_receive",
      "title": "Receive replies",
      "method": "POST",
      "endpoint": "/news",
      "description": "Receive messages/replies from other agents (agent may act on them, but never replies)"
    }
  ],
  "protocols": {
    "mcp": { "available": true, "endpoint": "/mcp" },
    "a2a": { "available": false }
  }
}

Capability fields. Each capability may include: id, title, method, endpoint, description, input_schema and output_schema (JSON Schema), content_types, output_formats, streaming, and sessions. On the talk capability, listing text/event-stream in output_formats and a streaming object signals SSE streaming is available; a sessions object signals session continuity.

Top-level protocols. The optional protocols object advertises whether related protocol endpoints are available; each entry is { "available": boolean, "endpoint"?: string }. RESTAP does not define MCP, A2A, etc.; this object just tells clients which integrations the agent exposes and where to reach them.

Packages. The optional packages array advertises client-side helpers (a claude-plugin with a SKILL.md, an npm-package, a pip-package, an sdk) that teach clients how to use the agent effectively.

Talk: POST /talk#

POST /talk is the one-directional entrypoint: the client sends a message, the agent triggers LLM processing, and replies.

curl -X POST https://example.com/api/agent/42/talk \
  -H "Content-Type: application/json" \
  -d '{"message": "What can you do?"}'
{ "reply": "I can echo text and reverse text. Check the catalog for details." }

Streaming (optional). A server MAY stream its /talk response incrementally via Server-Sent Events (SSE). This is negotiated with the HTTP Accept header; the response Content-Type tells the client what it actually got:

Request AcceptServer behavior
application/json (or none / */*)Return a complete JSON body (the default)
text/event-streamStream SSE if supported; otherwise 406 Not Acceptable
text/event-stream, application/jsonStream SSE if supported, else fall back to JSON

A streaming response is a sequence of SSE frames. Every streaming server MUST emit (and every streaming client MUST understand) these events: message.start, message.delta (a text chunk to append), message.end, error, and done (terminal). A minimal exchange is message.start → one or more message.deltamessage.enddone. Servers MAY also emit status, tool.start/tool.delta/tool.end, and artifact events; clients MUST safely ignore any they don't recognize. The tool.* and artifact events are presentational hints only, not a tool-invocation protocol.

Streaming applies to /talk only. /news never triggers a reply and never streams.

Sessions (optional). session_id is an opaque continuity token for multi-turn threads on /talk. A client MAY include one to continue a thread; if omitted, the server MAY mint one and MUST return it (in the JSON body as session_id, and in the SSE message.start/done events when streaming) so the client can echo it back next turn. Servers MAY be stateless and ignore it; that remains fully compliant. session_id is a correlation token, not authentication, and must be treated as a secret. RESTAP defines no session create/delete API, no required persistence, and no expiry.

News: the single read + write feed#

/news is a single bidirectional entrypoint for reading and writing updates, with the critical property that it never triggers a reply.

GET /news: read what's already happened (completed tasks, replies, notifications). Read-only, free to poll, no LLM cost. Supports a since parameter to fetch only new items: GET /news?since=1703012345000.

{
  "items": [
    { "type": "reply", "timestamp": 1703012400000, "from": "agent-b", "in_reply_to": "query_123", "message": "Best practices: use clear labels..." }
  ],
  "timestamp": 1703012350000
}

POST /news: write a message/reply for the agent to absorb. The agent MAY read it (as memory) and act on it internally, but it never sends a reply, which is what prevents infinite loops.

curl -X POST https://example.com/api/agent/42/news \
  -H "Content-Type: application/json" \
  -d '{"type": "reply", "from": "agent-b", "in_reply_to": "query_123", "message": "Here is my response..."}'
{ "status": "received", "news_id": "news_1", "message": "Message stored successfully" }

Anti-loop, end to end. The no-reply guarantee starts at the HTTP layer (a POST /news doesn't synchronously invoke the LLM or return a reply). But stored news is usually surfaced to the agent later, injected into its context. When it is, implementations SHOULD wrap the item with a system-level marker so the model doesn't mistake it for a prompt to answer, e.g. [NEWS — informational only. Do NOT reply to this.]. This keeps the guarantee holding all the way to the model, while still letting news inform future /talk answers.

Session-scoping (optional). A server whose news is per-conversation (not global to the agent) MAY require a session_id on /news. If it does, it MUST declare that in discovery and reject requests that omit it with 400 {"error":"missing_session_id"}. Servers whose news is global should accept /news without one.

ID Agents implementation on top of RESTAP#

Everything below is ID Agents' implementation: practical endpoints and conventions the ID Agents manager and agents layer on top of the RESTAP core. Any RESTAP-compliant agent is discoverable and addressable with just the core above; these extensions are specific to how ID Agents runs a local fleet.

In ID Agents, the manager daemon listens on port 4100 and each agent gets its own port starting at 4101 (4102, 4103, …). Each agent exposes the RESTAP core endpoints on its own port.

/talk-to: synchronous inter-agent messaging (extension)#

The primary inter-agent endpoint in ID Agents. Each agent exposes /talk-to on its own port. Unlike /talk, it is synchronous; it blocks until the target agent replies. This is not part of the RESTAP core; it's an ID Agents convenience for local fleets.

# Agent A (port 4101) asks Agent B a question, synchronously
curl -X POST http://localhost:4101/talk-to \
  -H "Content-Type: application/json" \
  -d '{"to": "agent-b", "message": "What are best practices for buttons?"}'
{ "ok": true, "reply": "Here are the best practices for buttons..." }

The manager routes the message to the target agent, waits for the reply, and returns it. Agents use this via the inter-agent skill.

/news-to: fire-and-forget (extension)#

The manager's /news-to endpoint sends a one-way notification to another agent without expecting a reply. Use it when you want to notify an agent but don't need a response.

curl -s -X POST http://localhost:4100/news-to \
  -H "Content-Type: application/json" \
  -d '{"to": "agent-b", "from": "agent-a", "message": "Heads up, the index rebuild finished."}'

noAutoReply flag (extension)#

When messages are triggered by schedules or other agents, ID Agents sets a noAutoReply flag so the receiving agent processes the message but does not automatically reply, reinforcing the RESTAP anti-loop guarantee. This applies to scheduled messages (heartbeats and calendar events) and to inter-agent notifications.

Manager endpoints (extension)#

In addition to the RESTAP core endpoints, the ID Agents manager exposes:

EndpointMethodPurpose
/agentsGETList all agents
/news-toPOSTFire-and-forget one-way notification (no reply)
/talk-toPOSTSynchronous inter-agent call (blocks until reply)
/remotePOSTExecute CLI commands programmatically
/tasksGETList all tasks
/tasksPOSTCreate a new task
/tasks/:nameGETGet a single task by name
/tasks/:name/claimPOSTClaim an unassigned task
/tasks/:name/donePOSTMark a task as completed
/tasks/:nameDELETEDelete a task
curl http://localhost:4100/agents

curl -X POST http://localhost:4100/remote \
  -H "Content-Type: application/json" \
  -d '{"command":"/status"}'

Tasks API (extension)#

The manager exposes REST endpoints for task management. Agents use these directly instead of going through /remote for task operations.

# Create
curl -X POST http://localhost:4100/tasks \
  -H "Content-Type: application/json" \
  -d '{"name": "fix-overflow", "title": "Fix the overflow bug"}'

# List / get
curl http://localhost:4100/tasks
curl http://localhost:4100/tasks/fix-overflow

# Claim (todo → doing) and complete (→ done)
curl -X POST http://localhost:4100/tasks/fix-overflow/claim \
  -H "Content-Type: application/json" -d '{"owner": "dev-backend"}'
curl -X POST http://localhost:4100/tasks/fix-overflow/done

# Delete
curl -X DELETE http://localhost:4100/tasks/fix-overflow
StatusMeaning
todoUnclaimed, no agent assigned yet
doingIn progress, an agent has claimed it
doneCompleted

Communication patterns#

Synchronous agent-to-agent (primary). Agents use /talk-to on their own port, which blocks until the reply arrives; the simplest, most reliable approach for a local fleet:

Agent A → POST /talk-to (own port) → Manager routes to Agent B → reply returned

Asynchronous (external clients). For external clients or longer tasks, use the RESTAP core /talk + /news polling flow:

1. Client → POST /talk → Agent (returns immediately)
2. Client ← GET  /news ← Agent (poll until the completion item appears)
# Send, then poll for the reply
RESPONSE=$(curl -s -X POST http://localhost:4102/talk \
  -H "Content-Type: application/json" \
  -d '{"message": "What are best practices for buttons?"}')

sleep 3
curl -s "http://localhost:4102/news?since=0"

Polling best practices#

  1. Start with 2 to 5 second intervals for typical tasks.
  2. Use the since parameter to fetch only new items: /news?since=1703012345000.
  3. Use exponential backoff for long-running tasks.
  4. Check for both completion and failure item types.
  5. Set reasonable timeouts (30 to 120 seconds depending on the task).

Further reading#