Discovery
GET /.well-known/agent-card.json — public, no auth. Declares the endpoint URL
and the bearer auth scheme a client should use.
Authentication
Authenticate using a Cyberwave API Token from your profile page. The token must belong to the target workspace and include thea2a scope key. Send it on every request:
Endpoint
POST /api/v1/a2a — single JSON-RPC 2.0 endpoint ({jsonrpc, id, method, params}).
Auth: Authorization: Bearer <APIToken>, a workspace-scoped partner token (not a
user session token). params.metadata.storeId selects the target Environment
by the last segment of its slug: for acme/envs/aisle-a3, send aisle-a3.
The Environment must belong to the token’s workspace.
SendMessage (non-streaming) and GetExtendedAgentCard are recognized but return
UnsupportedOperationError — not implemented yet.
Start a task and receive updates
curl -N disables output buffering so events appear as they
arrive. Keep the connection open until you receive finished.
A successful streaming response has Content-Type: text/event-stream. Planning
and initial dispatch happen before streaming begins, so the first event may take
time to arrive. Check both the HTTP status and response content type: a rejected
request can return JSON instead of SSE, including a JSON-RPC error with HTTP 200.
Event format
Each SSE frame contains anevent: name and a data: line containing JSON,
followed by a blank line. The JSON has its own type field; task payloads are
nested inside data.
These are Cyberwave’s current event envelopes. Streaming frames do not wrap
their payload in a JSON-RPC
result object. Parse the shapes below when
integrating a client, even if your A2A library expects standard status or
artifact update envelopes.
For example, a successful task could emit the following frames. The identifiers,
robot name, and summary are illustrative:
task event contains the current state, which may already be terminal
for a fast task or a retried request. Do not require an update before accepting
finished. Treat finished as the authoritative end of the task; final is only
transport bookkeeping and uses the internal lowercase state spelling.
Progress fields and task states
Anupdate is a full current snapshot, not a text delta or a token stream:
reasoning.centralcontains task-level progress entries.reasoning.robotslists robot assignments withrobotId,name,role,status, andsteps.- Robot
statusvalues are lowercase:queued,running,completed,failed,cancelled, orblocked. These differ from the task-level state values below.
central and steps empty; clients should
show task and robot status even when there is no explanatory text. videos is
also currently an empty list; video results are not populated by this API.
A
finished event can report any terminal outcome, so check data.state before
marking a task successful.
Stream lifetime and client handling
Progress is checked approximately once a second, and updates are emitted only when task state, robot status, or progress-entry counts change. This is a status stream, not a continuous telemetry feed. Ignore SSE comment lines such as: keep-alive; they are not task events.
The current stream loop has a 30-minute limit. If the task is still nonterminal
at that limit, it is marked failed, in-flight assignments are canceled, and
finished reports TASK_STATE_FAILED with a timeout summary.
Use an HTTP client that supports a streaming POST with an Authorization header.
The browser’s native EventSource interface does not support this request shape.
When implementing a reader:
- Buffer incoming bytes until complete SSE frames are available; network chunks do not necessarily align with frames.
- Parse the JSON from each frame’s
data:field and dispatch ontype. - Save
data.idfromtaskand update progress from subsequentupdateevents. - On
finished, store the state and summary and close the reader. - If the stream errors or closes before
finished, check the task and reconnect as described below. A disconnected stream does not mean the task completed or was canceled.
Environment control policy
The selected Environment controls which execution routes A2A may use:
Every Environment, new or existing, defaults to Direct twin control and
workflows so upgrading never blocks a routine that worked before.
Environment authors configure this under Environment settings → Advanced →
Control plane access. Choosing Direct twin control only disables workflow
execution everywhere for that Environment, not only for A2A — the same manual
trigger used by the web app’s Run button and the SDK is gated too. In
workflows-only mode, nodes inside an allowed workflow
may still command twins; the restriction applies to the external A2A request,
not to the workflow’s authored behavior.
Only active, non-deleted workflows attached to the selected Environment, visible
to the caller, and containing an enabled manual trigger are candidates. If no
permitted route matches the request, the task finishes in the
rejected state
without dispatching a twin command.
When A2A starts a workflow, the task’s metadata.workflow object identifies the
selected workflow and execution:
working while the workflow runs and mirrors its terminal
state: workflow success becomes completed, an execution error becomes
failed, and cancellation becomes canceled.
Reconnect to an existing task
Once you have a task ID, POSTSubscribeToTask (or tasks/resubscribe) to the
same endpoint with params.id. Use an API token from the same workspace:
data.id from your initial task event. The
top-level JSON-RPC id identifies this request; params.id identifies the task.
The reattached stream starts immediately with an update containing the current
snapshot, not another task event. It then emits changed snapshots and a
terminal finished event using the same shapes as the original stream.
Resubscription does not replay missed events. There are no SSE event IDs or
Last-Event-ID replay support. If the task is already terminal,
SubscribeToTask returns -32004 (UnsupportedOperationError) as JSON; use
GetTask to read its state. A task can finish between polling and reconnecting,
so handle this error even after a nonterminal poll result.
If the original connection closed before you received a task ID, retry
message/stream with the original message.messageId and request payload to
recover the existing task without dispatching it again.
Poll or cancel a task
GetTask (tasks/get) returns a normal JSON response rather than a stream:
result has the same task envelope as the initial stream event:
GetTask returns task state and robot identities; it does not return the
update reasoning snapshot or the finished summary and videos. Persist the
finished payload if your integration needs those results later.
To cancel, send the same request with method: "tasks/cancel" (alias
CancelTask). It returns a task envelope with TASK_STATE_CANCELED; an attached
stream reports the terminal outcome through finished. Canceling an already
terminal task returns -32002 (TaskNotCancelableError). Closing your client
connection is not a cancellation request.
Error handling
Standard JSON-RPC (-32700 parse error, -32600 invalid request, -32601
method not found, -32602 invalid params, -32603 internal error), plus
-32001 (task not found), -32002 (task not cancelable), -32004
(unsupported operation).
JSON-RPC errors normally return HTTP 200 with an error object instead of a
stream. For example, reconnecting after completion returns:
a2a
scope returns HTTP 403. Check these HTTP failures as well as JSON-RPC errors.
An error event after the SSE response has started is a transport error, not a
finished task outcome; poll the task to determine its state.
Retries and idempotency
Always sendmessage.messageId, using a unique value for each new logical task.
If a request is retried with the same messageId (e.g. after a dropped
SSE connection before a finished event arrived), the API returns the
original task instead of dispatching the same robots again. Deduplication is
scoped to the workspace, so keep the same ID for retries of one logical request
and use a new ID for each new task. A different messageId is always treated
as a new task, even if the text is identical to a previous request.