Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

ironflow a2a

Expose agents to remote agents over the Agent2Agent (A2A) protocol, version 1.0.

Terminal window
ironflow a2a --agent <function-id> [flags]

ironflow a2a is a separate process. It calls the Ironflow server through the public API. It keeps no state: one A2A task is one Ironflow run, and the task ID is the run ID. You can restart the adapter while tasks run.

Each exposed agent has its own address:

AddressContent
<public-url>/agents/<function-id>/.well-known/agent-card.jsonAgent Card. No token is necessary.
<public-url>/agents/<function-id>JSON-RPC binding
<public-url>/agents/<function-id>/...HTTP+JSON binding
FlagDefaultDescription
--agent(none)Function ID of an agent to expose. Repeatable. At least one is necessary.
--caller-input(none)An event name that remote callers can send to a waiting run of the function, as <function-id>/<event-name>. Use the name that the agent passes to approve(). Repeatable.
--server-url(none)Ironflow server URL. When not set, the adapter uses IRONFLOW_SERVER_URL, then http://localhost:9123.
--api-keyIRONFLOW_API_KEYAPI key for the Ironflow server. The key needs the developer role.
--host127.0.0.1Host to listen on.
--port9124Port to listen on.
--public-urlhttp://<host>:<port>Base URL that remote callers use. The adapter writes it into the Agent Cards.
VariableDescription
IRONFLOW_A2A_BEARER_TOKENRequired. Remote callers send this token as Authorization: Bearer <token>.
IRONFLOW_SERVER_URLIronflow server URL, when --server-url is not set.
IRONFLOW_API_KEYAPI key for the Ironflow server, when --api-key is not set.

Remote callers present the bearer token. The adapter calls the Ironflow server with its own API key. Callers do not get an Ironflow API key, so they can reach only the exposed agents.

All callers share one token. Each caller can read and cancel the tasks of the other callers for the same agent.

A caller reaches only the runs that the adapter started. A run of the same function that staff, an event or a schedule started is not an A2A task: the adapter does not list it and reports it as not found.

The adapter serves plain HTTP. For a host that is not loopback, put a TLS proxy in front of the adapter and set --public-url.

On a loopback listener, a request with a Host header that is not loopback and is not the --public-url host gets 403. This protects against DNS rebinding.

Ironflow runA2A task
Waiting for capacityTASK_STATE_SUBMITTED
Running or pausedTASK_STATE_WORKING
Waiting for an event name that --caller-input listsTASK_STATE_INPUT_REQUIRED
Waiting for any other reasonTASK_STATE_WORKING
Completed, failed, cancelledTASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED
  • SendMessage waits 30 seconds at most for the task to finish. Then it returns the task in its current state. Use GetTask or SubscribeToTask to follow a long task.
  • Streams send task states and the names of completed steps. They do not send model tokens.
  • Push notifications, the gRPC binding and the extended Agent Card are not available.
  • A file part must carry a URL. Inline file bytes are not accepted.
  • A message must have a messageId, a role and at least one part. The messageId is the idempotency key: a retry with the same ID gets the same task.
  • One request can be 4 MiB at most.
  • An open stream sends a keep-alive comment line every 15 seconds.
  • ListTasks sets totalSize to the number of tasks in the returned page. Use nextPageToken to get more pages.
  • SubscribeToTask refuses a task that is already completed, failed or canceled.
  • A failed task carries the error message of the run. Do not put secrets or internal addresses in the errors that an agent returns.

See Expose an agent over A2A for a full example.