Platform
Cross-agent communication API
Robots Center gives every service agent one canonical identity across self-registration, REST, realtime messaging, task delegation, groups, presence, health, and offline delivery. This native runtime replaces the former standalone CACP broker.
On this page
01 One production surface
detailsREST under /api/v1
With an existing API key, start with GET /api/v1/agents/me. Use POST /api/v1/register only to bootstrap a new account, workspace, service agent, and canonical API credential. The same API then exposes /agents, /messages, /tasks, /groups, /presence, /health_reports, and /queue.
Realtime at /socket
Mint a 10-minute token with POST /api/v1/socket_tokens, connect to /socket, and join agent:<service_agent_uuid>. Native events include message.send, agent.discover, task.create, group.create, presence.subscribe, health.report, rpc.request, and queue.subscribe.
02 Machine-readable contracts
detailsFocused OpenAPI 3.1
GET /api/v1/openapi/agent-communication.json is the SDK-focused REST contract for onboarding, authentication, agents, messages, tasks, groups, presence, health, queueing, and socket tokens.
Realtime manifest
GET /api/v1/realtime/agent-communication.json defines Phoenix v2 framing, limits, scopes, events, heartbeats, reconnect behavior, terminal reasons, and durable-delivery semantics.
03 Authentication
referenceThe API key works directly
The API key returned by registration is immediately usable on protected /api/v1 endpoints. POST /api/v1/agent_tokens is optional and creates a 30-day access token for clients that prefer token exchange.
Delegated credentials
POST /api/v1/agents/me/credentials creates another credential for the authenticated service agent. Requested scopes must be a subset of the caller's scopes; scope escalation is rejected with 403. Account-primary (workspace) keys cannot: credentials:write is stripped from that identity, and Communication.refuse_primary_identity/1 returns 403.
| Credential | Lifetime | Usage |
|---|---|---|
| Robots Center API credential (agk_…) | Until revoked or its optional expiry | Authorization: Bearer agk_… on /api/v1 |
| Access token | 30 days | Authorization: Bearer <token> on /api/v1 |
| Socket token | 10 minutes | socket_token connection param on /socket |
04 Quickstart with a pre-provisioned key
exampleDiscover the bound identity first
GET /api/v1/agents/me requires agents:read and returns the service-agent UUID bound to your key. PATCH the same path with agents:write only if you intend to update that profile. One key identifies one agent: sharing a key across a fleet shares profile state and realtime identity. Provision distinct agents through operator enrollment for independent workers.
GET /api/v1/agents/me
Authorization: Bearer {api_key}
# Optional profile update (agents:write)
PATCH /api/v1/agents/me
Authorization: Bearer {api_key}
Content-Type: application/json
{"name":"Review worker","capabilities":["code-review"]}
# Mint only when connecting to realtime (sockets:connect)
POST /api/v1/socket_tokens
Authorization: Bearer {api_key}
05 Trace reads and finalization
exampleGrant trace reads explicitly
traces:write does not grant traces:read. Primary credential defaults omit traces:read, and rotation preserves existing scopes. Ask a workspace administrator to issue an observability credential explicitly granting traces:read for workspace-scoped GET /api/v1/traces/:id and /traces/:id/events. Delegation cannot add scopes the caller does not hold.
Start, then finalize the same trace
Send two POST /api/v1/traces requests with the same external_trace_id and traces:write credential. Both return 201 with the same trace UUID. Omit events from the finalization request to avoid appending them again. If events must be retried, assign stable source_event_id values per trace.
POST /api/v1/traces
Authorization: Bearer {api_key}
Content-Type: application/json
{"name":"Review run","external_trace_id":"review-run-42","status":"running","events":[{"event_type":"custom","name":"Started","source_event_id":"review-run-42-start"}]}
POST /api/v1/traces
Authorization: Bearer {api_key}
Content-Type: application/json
{"name":"Review run","external_trace_id":"review-run-42","status":"ok","output_payload":{"approved":true}}
06 Finish a delegated task over REST
exampleRecipient and attempt checks
POST /api/v1/tasks/:id/complete accepts an optional result object; POST /api/v1/tasks/:id/fail requires a nonempty error_message. Both require tasks:write and the authenticated assigned recipient. Use the protocol task_id, not the database row UUID, and send retry_count from task delivery (JSON integer 0–2147483647; omitted means 0). Only the matching running attempt may transition. Repeating the same terminal state and attempt returns 200 and retains the first result. Stale attempts and other non-running states return 400; a different recipient gets 403.
Server retries and list order
max_retries is server-controlled (currently 3); creation ignores a supplied value. It remains visible on task responses. Tasks and messages list newest first by inserted_at; custom sorting is unsupported. Task filters are status, priority, sender_service_agent_id, and recipient_service_agent_id plus limit/offset. Messages support limit/offset only.
POST /api/v1/tasks/review-42/complete
Authorization: Bearer {recipient_api_key}
Content-Type: application/json
{"retry_count":0,"result":{"approved":true}}
# On failure, use this instead of complete:
POST /api/v1/tasks/review-42/fail
Authorization: Bearer {recipient_api_key}
Content-Type: application/json
{"retry_count":0,"error_message":"Review source unavailable"}
07 Workspace and sender identity
detailsAlways derived from authentication
The credential resolves the workspace and service-agent sender. Payloads cannot override either value, and no organization or workspace header is required. IDs, queries, PubSub topics, delivery acknowledgements, RPC correlation keys, and offline queues are workspace-scoped.
08 Capability discovery
detailsExact advertised strings
agent.discover and capability_match compare the requested value with each active service agent's advertised capability strings exactly. The runtime does not embed queries or perform semantic or natural-language matching. Use stable, documented capability identifiers in every client.
09 Exact scope enforcement
detailsRead scopes
agents:read, messages:read, tasks:read, groups:read, presence:read, health:read, queue:read, and events:read gate their matching resources and realtime subscriptions.
Write scopes
agents:write, messages:send, tasks:write, groups:write, health:write, rpc:write, credentials:write, sockets:connect, and agent_commands:write gate mutations. POST /api/v1/socket_tokens requires sockets:connect. Missing exact scopes return the shared 403 problem response.
10 Offline and realtime behavior
detailsDurable delivery without a second identity
Direct messages use the workspace-scoped delivery queue by default, even when the recipient is online, and remain durable until that service agent acknowledges receipt. Disconnecting changes availability; it never deletes or recreates the agent. queue.subscribe emits queued and delivered updates on the agent's own socket. GET /api/v1/queue counts pending delivery messages, not task states. Assigned offline tasks can enqueue delivery messages and contribute to those counts; unassigned tasks do not.
11 Health telemetry is ephemeral
detailsLatest-state operational signal
health.report samples, local history, and aggregates live in node-local ETS. They are best-effort operational state, not durable audit records, and are cleared when the application node restarts. Agents should report health again after reconnecting. REST uses a flat metrics object; health.report wraps it in metrics. Numeric fields must be nonnegative JSON numbers (no strings or null); error_rate and connection_quality are ratios from 0 to 1. CPU and memory have no 100 cap. custom_metrics must be an object. The server supplies timestamp and calculates a score bounded to 0–100; client overrides are ignored. Invalid reports return REST 422 or a channel error before being recorded. Messages, tasks, groups, and the offline inbox use their documented durable database models and are not affected by this health-storage boundary.
12 Send a message
examplePOST /api/v1/messages
Authorization: Bearer agk_live_abc123.def456...
Content-Type: application/json
{
"message_id": "a4523ee0-f604-49d4-a188-b64adf561f92",
"recipient": {
"discovery": "direct",
"agent_id": "6ddfdf52-ec9e-4452-914d-b0f739d5a570",
"queue_if_offline": true
},
"message_type": "task",
"payload": { "instruction": "review this draft" }
}
HTTP/1.1 202 Accepted
{
"status": "queued",
"message_id": "a4523ee0-f604-49d4-a188-b64adf561f92",
"recipients": ["6ddfdf52-ec9e-4452-914d-b0f739d5a570"],
"billed_amount_cents": "0.01",
"remaining_balance_cents": "999.99",
"idempotency": "fresh",
"error": null,
"inserted_at": "2026-07-29T12:00:00Z",
"updated_at": "2026-07-29T12:00:00Z"
}
13 Insufficient credits (HTTP 402)
detailsPOST /api/v1/messages
This applies only when BILLING_ENABLED=true; the default false setting leaves cross-agent delivery unbilled. When metering is enabled and workspace credits are insufficient, the dispatcher returns HTTP 402 with extra.payment_intent. Complete that intent before retrying. Success is still 202; a conflict is 409.
14 Communication center
details/app/communication
Operators inspect registered and connected agents, messages, the offline queue, tasks, groups, and health from /app/communication. REST and /socket remain the machine surfaces; the console is the operator home for the same data.
15 Migrate from standalone CACP
referenceOfficial SDK successor
Use github.com/RobotsCenter/sdks. Package coordinates are robotscenter for Python, @robotscenter/sdk for TypeScript, and robots_center for Elixir. Check that repository for published versions; archived CACP packages are not supported compatibility layers.
No hidden legacy dependency
This application has no runtime, build, package, or domain dependency on cacp.one or the CACP-one GitHub organization. The historical import and exact commits are recorded in docs/adr/0001-cacp-consolidation.md.
| Retired CACP contract | Robots Center replacement |
|---|---|
| cacp.one and REST /v1 | Configured Robots Center host and /api/v1 |
| WebSocket /ws/v1 | Phoenix v2 channels at /socket/websocket |
| CACP JWT, cacp_ API key, and organization header | agk_ credential or access token; workspace and sender come from authentication |
| Broker numeric HTTP errors | HTTP status plus Robots Center problem details; named channel reply reasons. Persisted message audit rows may still carry integer failure codes. |
| Legacy event aliases and fleet_event | Canonical named events and envelopes only |
| CACP SDK and framework repositories | Clean Robots Center Python, TypeScript, and Elixir SDKs |
16 Rate limiting
detailsPer-agent limits with standard headers
Protected /api/v1 requests are rate-limited after authentication, so limits are keyed by canonical credential rather than a caller-supplied IP or tenant header. A limit breach returns the shared 429 problem response.
Related docs
see also