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-agentscore: 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#
| Endpoint | Method | Purpose | Triggers a reply? |
|---|---|---|---|
/.well-known/restap.json | GET | Discovery: the agent's catalog | No |
/talk | POST | One-directional entrypoint: client → agent, triggers an LLM response | Yes |
/news | GET | Read updates (read-only) | No |
/news | POST | Write messages/replies the agent may absorb or act on | No, 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:
| Endpoint | Direction | Sends a reply? | Reaches the LLM? | Use case |
|---|---|---|---|---|
POST /talk | One-directional (client → agent) | ✅ Yes, the agent replies with LLM output (optionally streamed via SSE; supports session_id) | Yes, it is the prompt the agent answers | Send tasks/questions that need an answer; hold a conversation |
GET /news | Read | ❌ No, read-only retrieval | N/A | Poll for updates, read what's already happened |
POST /news | Write | ❌ No, must not reply | Yes, as memory: the item MAY be read by the model and the agent MAY act on it internally | Deliver 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 Accept | Server behavior |
|---|---|
application/json (or none / */*) | Return a complete JSON body (the default) |
text/event-stream | Stream SSE if supported; otherwise 406 Not Acceptable |
text/event-stream, application/json | Stream 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.delta → message.end → done. 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:
| Endpoint | Method | Purpose |
|---|---|---|
/agents | GET | List all agents |
/news-to | POST | Fire-and-forget one-way notification (no reply) |
/talk-to | POST | Synchronous inter-agent call (blocks until reply) |
/remote | POST | Execute CLI commands programmatically |
/tasks | GET | List all tasks |
/tasks | POST | Create a new task |
/tasks/:name | GET | Get a single task by name |
/tasks/:name/claim | POST | Claim an unassigned task |
/tasks/:name/done | POST | Mark a task as completed |
/tasks/:name | DELETE | Delete 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
| Status | Meaning |
|---|---|
todo | Unclaimed, no agent assigned yet |
doing | In progress, an agent has claimed it |
done | Completed |
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#
- Start with 2 to 5 second intervals for typical tasks.
- Use the
sinceparameter to fetch only new items:/news?since=1703012345000. - Use exponential backoff for long-running tasks.
- Check for both completion and failure item types.
- Set reasonable timeouts (30 to 120 seconds depending on the task).
Further reading#
- RESTAP specification: the full protocol, the authoritative source.