A2A agents
Publish Scrydon workflows as A2A 1.0 agents, invoke them from Cortex or another A2A client, and call external agents from a workflow.
Scrydon supports the Agent2Agent (A2A) protocol in both directions:
- Publish a workflow as its own A2A agent.
- Use the A2A block to discover and call an external agent.
The supported A2A 1.0 bindings are JSON-RPC and HTTP+JSON, including streaming over SSE. Optional A2A 0.3 compatibility is available for existing clients and agents. Scrydon does not advertise or claim the gRPC binding.
Publish a workflow as an agent
Each workflow publication is one independently addressable A2A agent. An organization can publish any number of workflows, and each gets its own stable Agent ID, Agent Card, task namespace, visibility, and pinned deployment version.
The Deployments panel contains one Agent endpoints section per workflow — not one per environment. A Serves environment field in the A2A configuration selects which environment's deployment the agent executes (defaults to the highest deployed environment; the development draft can be selected explicitly). Published agents appear as rows in Agent endpoints: A2A Agent · <visibility> · serves <environment>. When nothing is published yet, a single quiet row with a Publish button is shown instead.
- Open the workflow in the editor. The workflow must contain an enabled Response block.
- Click Deploy in the editor toolbar (it reads Versions once everything is deployed) to open the Deployments panel. The panel shows a vertical pipeline stepper — one row per environment with version, time, deployed-by, and a promote button.
- If no deployment exists for the target environment yet, click the Promote button on the relevant environment row.
- Scroll to the Agent endpoints section at the bottom of the panel and click Publish (or click an existing A2A agent row to reconfigure it).
- In the A2A configuration, choose the Serves environment, configure the basic Agent Card (and optionally an extended Agent Card), and choose API-key and/or bearer-token authentication.
- Keep the default Private visibility, or explicitly make the agent public and discoverable.
- Save, then copy the Agent Card or protocol endpoint shown in the view.
Publishing always pins an immutable deployment snapshot. Editing the workflow canvas does not change the running agent. Promote a newer version and update the pinned deployment in the A2A configuration when you are ready to republish.
A published agent pins a deployment, so its workflow cannot be undeployed. Undeploy in the Monitor, like DELETE /api/v1/workflows/{id}/deploy, answers 409 with the code WORKFLOW_PUBLISHED and names the agent. Nothing changes: the workflow stays deployed and its webhooks keep receiving events. Setting the agent's visibility to Disabled stops serving it, but the deployment stays pinned, so undeploying is still refused.
Agent icons
Scrydon generates a unique monogram icon for every published agent automatically. The icon appears in the Agent Card returned to external A2A clients. Custom icon URLs can still be set programmatically via the publication API (basicCard.iconUrl), but are no longer part of the UI.
Workspace environments are user-defined. Their names and read-only setting do not determine A2A visibility: a workflow in any writable or read-only environment may be disabled, private, or public. Credentials remain bound to the publication's exact workspace environment.
Visibility
| Visibility | Agent Card | Public catalog | Protocol operations |
|---|---|---|---|
| Disabled | Unavailable | Hidden | Unavailable |
| Private (default) | Requires authentication | Hidden | Requires authentication and workflow authorization |
| Public | Public basic card | Listed | Still requires authentication and workflow authorization |
Public visibility makes discovery public; it does not make workflow execution or task data public. An authenticated extended Agent Card is never returned by public discovery.
The public organization catalog is paginated at:
GET /api/a2a/catalog/{organizationId}?pageSize=25&pageToken=...The authenticated A2A panel also lists the organization's publications across all environments, including disabled and private agents.
Agent Cards and endpoints
The A2A configuration view provides these URLs:
| Endpoint | Purpose |
|---|---|
/api/a2a/agents/{agentId}/.well-known/agent-card.json | Basic Agent Card discovery |
/api/a2a/agents/{agentId} | A2A 1.0 JSON-RPC |
/api/a2a/agents/{agentId}/... | A2A 1.0 HTTP+JSON resources and methods |
/api/a2a/jwks | Public keys for Agent Card signature verification |
The basic card describes the agent's name, version, provider, documentation, supported media types, skills, security schemes, bindings, and capabilities. Enable the extended card when authenticated callers need a richer description than public discovery should expose.
Cards are signed with ES256 and include their JWK Set URL. Public cards support ETag, Last-Modified, and cache revalidation. Private cards use private, no-store caching.
Authentication and task isolation
Enable at least one publication authentication scheme:
- API key: send the environment-bound workspace key in
X-API-Key. - Bearer: send an OAuth/OIDC token in
Authorization: Bearer …. Read operations requireworkflows:read; mutations and execution requireworkflows:write.
Every task is scoped to the publication and the authenticated principal. A different principal, organization, workspace, or workspace environment receives the same not-found response as a nonexistent task. Task IDs therefore cannot be used to enumerate another caller's work.
Call the agent
Every protocol request should send the selected interface's advertised version:
curl -X POST 'https://scrydon.example/api/a2a/agents/AGENT_ID' \
-H 'A2A-Version: 1.0' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: YOUR_WORKSPACE_KEY' \
-d '{
"jsonrpc": "2.0",
"id": "request-1",
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "message-1",
"parts": [{ "text": "Create the quarterly summary", "mediaType": "text/plain" }]
}
}
}'The equivalent HTTP+JSON call uses the same protocol base URL:
curl -X POST 'https://scrydon.example/api/a2a/agents/AGENT_ID/message:send' \
-H 'A2A-Version: 1.0' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"message": {
"role": "ROLE_USER",
"messageId": "message-1",
"parts": [{ "text": "Create the quarterly summary", "mediaType": "text/plain" }]
}
}'SendMessage returns either a task or a direct agent message. For a task, poll GET /tasks/{taskId}, subscribe with POST /tasks/{taskId}:subscribe, or use a push-notification configuration.
Prove interoperability with the official JavaScript SDK
This is the strongest independent smoke test for a private publication because it discovers the Agent Card, negotiates an advertised transport, and sends an A2A 1.0 message without using a Scrydon session cookie.
- Create an API key in the same workspace environment as the publication.
- Copy the Protocol URL from the A2A configuration view.
- In an empty directory, install the stable official SDK:
bun add @a2a-js/sdk@1.0.0- Save this as
a2a-proof.mjs:
import { randomUUID } from "node:crypto";
import {
Message,
Role,
SendMessageConfiguration,
Task,
} from "@a2a-js/sdk";
import {
ClientFactory,
ClientFactoryOptions,
DefaultAgentCardResolver,
JsonRpcTransportFactory,
RestTransportFactory,
} from "@a2a-js/sdk/client";
const agentUrl = process.env.A2A_AGENT_URL;
const apiKey = process.env.A2A_API_KEY;
if (!agentUrl || !apiKey) {
throw new Error("Set A2A_AGENT_URL and A2A_API_KEY");
}
const authenticatedFetch = async (input, init) => {
const original =
input instanceof Request ? new Request(input, init) : new Request(input, init);
const headers = new Headers(original.headers);
headers.set("X-API-Key", apiKey);
return fetch(new Request(original, { headers, redirect: "error" }));
};
const factory = new ClientFactory(
ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
cardResolver: new DefaultAgentCardResolver({
fetchImpl: authenticatedFetch,
}),
transports: [
new JsonRpcTransportFactory({ fetchImpl: authenticatedFetch }),
new RestTransportFactory({ fetchImpl: authenticatedFetch }),
],
}),
);
const client = await factory.createFromUrl(`${agentUrl.replace(/\/$/, "")}/`);
const card = await client.getAgentCard();
console.log(
"Negotiated",
client.protocolVersion,
card.supportedInterfaces.map((item) => item.protocolBinding),
);
const result = await client.sendMessage({
tenant: "",
message: Message.fromJSON({
messageId: randomUUID(),
role: Role.ROLE_USER,
parts: [
{
text: "Run the A2A interoperability proof",
mediaType: "text/plain",
},
],
}),
configuration: SendMessageConfiguration.fromJSON({
acceptedOutputModes: ["application/json", "text/plain"],
returnImmediately: false,
}),
});
console.dir(
"id" in result ? Task.toJSON(result) : Message.toJSON(result),
{ depth: null },
);- Run it:
A2A_AGENT_URL='https://scrydon.example/api/a2a/agents/AGENT_ID' \
A2A_API_KEY='YOUR_WORKSPACE_KEY' \
bun run a2a-proof.mjsThe first line should report protocol 1.0 and the advertised JSONRPC and HTTP+JSON bindings. The final value is either an A2A task or a direct agent message. A blocking workflow normally returns a task whose final state is TASK_STATE_COMPLETED.
The official A2A Inspector is useful for viewing an Agent Card, basic validation, and inspecting raw JSON-RPC. Its authentication support is not complete, so it cannot currently prove an authenticated Scrydon execution by itself. Use the official SDK example above or Cortex for the end-to-end invocation.
Invoke published agents from Cortex
Cortex can discover and call Scrydon-published agents directly. It uses the active workspace as the discovery boundary and the active workspace environment as the calling boundary; it does not accept arbitrary external A2A URLs.
- Publish one or more workflows as Private or Public A2A agents. Disabled agents are excluded.
- Switch Cortex to the same workspace. Agents published for any environment in that workspace are visible.
- Ask Cortex to find the agent by name, description, skill, or tag and tell it what to do. For example:
Find the A2A agent named "Quarterly reporting" and ask it to create the Q2 summary.- Cortex first runs
a2a_agent_search. Callable agents (serving the active environment) appear in the main results. Agents published for other environments are listed separately underelsewhere; Cortex will suggest switching the active environment to reach them. - Approve the
a2a_agent_callconfirmation. Calling an agent executes its pinned workflow deployment. - Inspect the returned A2A task or message in the conversation.
Cortex discovers both Private and Public publications across all environments of the active workspace because the signed-in user is already authorized there. Only agents whose publication serves the active workspace environment are callable; agents serving a different environment are reported but cannot be called until the user switches to that environment. Cortex revalidates the selected Agent ID immediately before each call.
Supported server operations
| Operation | JSON-RPC method | HTTP+JSON |
|---|---|---|
| Send a message | SendMessage | POST /message:send |
| Send and stream | SendStreamingMessage | POST /message:stream |
| Get a task | GetTask | GET /tasks/{id} |
| List tasks | ListTasks | GET /tasks |
| Cancel a task | CancelTask | POST /tasks/{id}:cancel |
| Subscribe to a task | SubscribeToTask | POST /tasks/{id}:subscribe |
| Create push configuration | CreateTaskPushNotificationConfig | POST /tasks/{id}/pushNotificationConfigs |
| Get push configuration | GetTaskPushNotificationConfig | GET /tasks/{id}/pushNotificationConfigs/{configId} |
| List push configurations | ListTaskPushNotificationConfigs | GET /tasks/{id}/pushNotificationConfigs |
| Delete push configuration | DeleteTaskPushNotificationConfig | DELETE /tasks/{id}/pushNotificationConfigs/{configId} |
| Get the extended card | GetExtendedAgentCard | GET /extendedAgentCard |
Both bindings use the A2A 1.0 task states, pagination, history controls, message continuation, artifacts, service parameters, extensions, canonical errors, and google.rpc.ErrorInfo details. Streaming uses text/event-stream and preserves the binding's response envelope.
When 0.3 compatibility is enabled, the Agent Card also advertises compatible JSON-RPC and HTTP+JSON interfaces and translates legacy methods and payloads through the official SDK.
Workflow input and output
The workflow receives the normalized request under a2a:
{
"a2a": {
"message": { "messageId": "message-1", "role": "ROLE_USER", "parts": [] },
"configuration": {},
"metadata": {}
}
}The selected Response block becomes the agent response:
- A string becomes a
text/plainagent message. - Any other JSON value becomes an
application/jsondata part when the caller accepts it. - For complete control, return
{ "a2a": { "message": ..., "artifacts": [...] } }as the Response data. - A Response status of 400 or higher fails the task without exposing internal node output.
Workflow pauses and human-review steps project to TASK_STATE_INPUT_REQUIRED. Continue the same task by sending another message with its taskId and matching contextId. Terminal tasks reject follow-up messages and subscriptions with the canonical A2A error.
Inbound messages and projected output pass through the organization's DLP policy. Execution, cancellation, publication changes, push configuration changes, and dead-letter delivery are audit logged without message bodies or credentials.
Payload limits
| Limit | Value |
|---|---|
| Serialized request or response | 6 MiB |
| Parts per message or artifact | 32 |
| Total response parts | 128 |
| Artifacts per response | 32 |
| Text or raw bytes per part | 1 MiB |
| Raw bytes per request or response | 5 MiB |
| Raw-file storage per task | 20 MiB |
| Metadata object | 64 KiB, maximum nesting depth 12 |
| Returned history | 0–100 messages |
Raw file parts are moved to the execution file store before durable scheduling and rehydrated only inside the authorized task scope.
Push notifications
Push configurations support a callback token or an authentication scheme and credentials. In production, callbacks must use HTTPS. Scrydon resolves and pins public DNS targets, blocks private, loopback, link-local, and cloud-metadata destinations, and revalidates delivery targets to prevent DNS rebinding.
Delivery preserves per-configuration ordering, retries retryable failures three times with backoff, and then marks the configuration dead-lettered. Callback secrets are encrypted at rest and never appear in audit logs.
Call external A2A agents
Add an A2A block to a workflow and choose one of the same discovery, messaging, streaming, task, subscription, push, or extended-card operations. Enter the agent's base or protocol URL. Scrydon looks for the Agent Card at {agent URL}/.well-known/agent-card.json first, then at the A2A 1.0 location on the host's root, then at the 0.3 locations as compatibility fallbacks. The first location is what lets one host serve many agents, as Scrydon itself does.
Outbound authentication supports:
- API keys in a header, query parameter, or cookie
- HTTP Basic
- Bearer tokens
- OAuth 2.0 access tokens
- OpenID Connect access tokens
You can also require a signed Agent Card, set a 100 ms–300 s timeout, and send non-reserved service parameters and A2A extension URIs. Mutual TLS is detected and rejected with a clear unsupported-authentication error.
Production outbound calls require HTTPS unless the operator explicitly enables A2A_ALLOW_INSECURE_HTTP=true. Every discovery URL, interface URL, redirect, and JWK Set URL is DNS-pinned and checked against private-address and metadata-host restrictions. Credentials and custom service headers are stripped on cross-origin redirects.
Operation outputs
Each operation returns the A2A result object itself, so reference its fields by path in later blocks:
| Operation | Returns | Useful fields |
|---|---|---|
| Send Message, Get Task, Cancel Task | A task, or a direct agent message from Send Message | id, contextId, status.state, status.message, artifacts, history; for a message messageId, role, parts |
| Send Streaming Message, Subscribe to Task | The collected stream | events, truncated |
| List Tasks | One page of tasks | tasks, nextPageToken, pageSize, totalSize |
| Get Agent Card, Get Extended Agent Card | The Agent Card | name, description, version, capabilities, skills, supportedInterfaces, securitySchemes |
| Create or Get Push Notification Config | The configuration | id, taskId, url, token, authentication |
| List Push Notification Configs | One page of configurations | configs, nextPageToken |
| Delete Push Notification Config | An acknowledgement | success, taskId, pushNotificationConfigId |
A task's state is at status.state, for example TASK_STATE_COMPLETED. An operation that fails, such as Get Task for an unknown task, fails the block with the agent's error.
Testing the A2A block
Every published Scrydon workflow agent is an A2A server, so the quickest agent to point the block at is one of your own:
-
Publish a workflow as an agent (see Publish a workflow as an agent) with API key authentication.
-
Create an API key in the same workspace environment as the publication.
-
Copy the protocol URL from the A2A configuration view. It has this shape:
https://YOUR_SCRYDON_HOST/api/a2a/agents/AGENT_IDThe Agent Card is at
https://YOUR_SCRYDON_HOST/api/a2a/agents/AGENT_ID/.well-known/agent-card.json. Fetch it with the key to confirm the agent answers:bashcurl 'https://YOUR_SCRYDON_HOST/api/a2a/agents/AGENT_ID/.well-known/agent-card.json' \ -H 'X-API-Key: YOUR_WORKSPACE_KEY' -
In a second workflow, add an A2A block, choose Get Agent Card, enter the protocol URL as the agent URL and the key as the API key, and run it. Then try Send Message, and follow up with Get Task or Subscribe to Task using the returned
id.
The agent URL must be reachable at a public address. The block refuses localhost, loopback, private-network, link-local, and cloud-metadata addresses by design, both for the URL you enter and for every address it resolves to. That is the same protection against server-side request forgery that applies to every outbound call, so an agent on your own machine or on a private network cannot be reached from the block. Test against a published agent on a public host instead.
Operator configuration
Production deployments must configure Agent Card signing with one of:
A2A_CARD_SIGNING_PRIVATE_JWKS: a JSON JWK Set or array. This supports key overlap and rotation.A2A_CARD_SIGNING_PRIVATE_JWK: a single private P-256 EC JWK for compatibility.
Set A2A_CARD_SIGNING_KEY_ID to select the active private key. Keys marked revoked, expired, or not yet active are not published or selected. A2A_PAGE_TOKEN_SECRET may be set to a dedicated secret of at least 32 characters; otherwise the configured platform auth secret signs scoped catalog and task page tokens.
Troubleshooting
| Symptom | Check |
|---|---|
401 Authentication required | Send one of the schemes advertised by the Agent Card. |
| Task or agent returns not found | Confirm the credential belongs to the publication's exact organization, workspace, and workspace environment. |
JSON-RPC -32005 or HTTP 415 | The message part's media type is not listed in the Agent Card input modes. |
JSON-RPC -32009 or HTTP 400 | Send an A2A-Version advertised by the selected interface. |
| Agent disappears | Confirm the publication is Private or Public rather than Disabled. |
| Workflow edits do not appear | Publish a new deployment version and repin the A2A publication. |
| Push callback is refused | Use a public HTTPS endpoint; private and metadata addresses are blocked. |
The A2A block reports agentUrl cannot point to localhost, agentUrl cannot point to private IP addresses, agentUrl cannot point to a blocked internal host, or resolves to a disallowed address | The agent URL is local, private, or a cloud-metadata host, or its name resolves to such an address. The block only calls public addresses; see Testing the A2A block. |
The A2A block reports Redirect blocked: agentUrl must use https:// protocol | An https:// agent redirected the block to a plain http:// address. The block refuses that downgrade; point it at the agent's final https:// URL. |
The A2A block reports Signed Agent Card JWK Set URL must use https:// | The agent signs its card but names a plain http:// key set (jku) to verify it with. Over plain http the key could be swapped in transit and a forged card would verify, so the block refuses it. The agent must publish its key set on https://. |