Browse documentation

Connect an agent with A2A

Let another AI agent delegate an investigation to Anectico over the Agent2Agent protocol.

Anectico speaks Agent2Agent (A2A). Another agent — yours, or a framework that speaks the protocol — can hand Anectico a question about a customer, an Issue, an agent run, or an incident, and receive a cited, evidence-grounded conclusion back as a task artifact.

This is the delegation surface. Use MCP when you want an agent to hold a set of tools and drive the investigation itself; use A2A when you want to hand the whole investigation over and get an answer.

Discover the agent

The agent card is public and needs no credential — an A2A client fetches it first to learn what the agent can do and how to authenticate:

curl https://app.anectico.com/.well-known/agent-card.json

It names one interface: a JSON-RPC endpoint at https://app.anectico.com/a2a. The card declares streaming: true and pushNotifications: true; the authenticated extended card is not offered, and GetExtendedAgentCard is refused rather than silently accepted. Read the card for what is offered rather than trusting this page: the card and the methods behind it move together.

Authenticate

The protocol endpoint requires a credential on every call. Either form works:

X-Anectico-API-Key: an_...
Authorization: Bearer an_...

Create a key with the investigate scope bundle — the same credential an MCP agent uses. See Scope recipes for the bundles and Revoke agent access for rotation.

A project-scoped key is pinned to its project. An organization-scoped credential selects the project per request through the A2A tenant field, which Anectico reads as the project id.

Every protocol request must also declare the representation and the protocol version advertised by the card. The current JSON-RPC interface requires both headers:

Content-Type: application/json
A2A-Version: 1.0

An absent or empty A2A-Version means version 0.3 under the A2A specification, which this interface does not advertise, so Anectico answers with VersionNotSupportedError. JSON field names use the protocol’s lower-camel-case spelling; for example, push configuration requests use taskId, not task_id.

Ask a question

Send one message. The text of the message is the question; every message starts a new task.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "tenant": "<project-id>",
    "message": {
      "role": "ROLE_USER",
      "messageId": "5f1c...",
      "parts": [{ "content": { "text": "Why did checkout keep failing for this user yesterday?" } }],
      "metadata": {
        "anectico.skill": "investigate-person",
        "anectico.anchor.ref": "person_01H..."
      }
    }
  }
}

Use SendStreamingMessage for the same call with progress delivered as it happens; it answers with Server-Sent Events.

Skills

anectico.skill chooses what the investigation is about, and anectico.anchor.ref supplies the identifier it needs. Omit both to ask an open-ended question scoped to the project.

anectico.skill anectico.anchor.ref Answers
investigate-person person id or raw distinct ID What happened to one customer: profile, activity, a recent cross-signal timeline, their errors, their session recordings
triage-issue error group id What is causing one Issue and who it hits: a representative occurrence, affected people, releases, the trace, replay and logs around it
explain-agent-run agent run id (its trace ID) What one of your own AI agent runs did: the run, its step transcript, and the customer it was serving
assess-incident-impact incident id An incident’s blast radius: the case file, its timeline, every attached signal, and the people those signals reached
verify-recovery incident id Whether an incident really recovered: the same evidence, asked whether the signals stopped and the affected customers succeeded afterwards

A skill this agent does not advertise is rejected, as is a skill whose identifier is missing. Ask the card for the current list rather than hard-coding one.

Read the answer

The task’s answer is an artifact with the ID conclusion: one text/markdown part holding the full, cited write-up. The task then moves to COMPLETED. Progress arrives as status updates before it; the conclusion artifact is the converged content, so a client that only reads artifacts loses nothing.

The conclusion artifact and terminal status both carry anectico.evidence.omissions metadata. Each entry has closed source and reason enum strings identifying evidence the model did not examine and whether the cause was scope, policy, availability, or the evidence limit. A present empty list means disclosure was evaluated and nothing was omitted. An absent metadata key means the conclusion producer did not populate the contract; do not treat that as complete. This is the agent contract — the matching Markdown sections remain for human readers, but clients do not need to parse them.

An answer is built from what your credential could read, and both that permission and the project’s current content policy are re-checked every time you read it back. If the credential’s scopes are narrowed after the task completes — a role change, or a key replaced by a narrower one — GetTask, ListTasks and SubscribeToTask return the task with its conclusion replaced by a short notice saying content of a kind your role no longer includes went into it. A later project-policy denial withholds the same conclusion for every reader, and an unavailable policy check fails closed with a distinct “could not be confirmed” notice. Everything else about the task stays: its state, its timing, and the question you asked. Re-run the investigation with the current credential to get an answer built from what you can see.

Follow up in the same conversation by sending another message with the same contextId. Each follow-up is a new task in that context and continues the same investigation.

The Investigator does not pause a task in INPUT_REQUIRED. It completes the current turn with the evidence available; when you want to clarify or add input, send a follow-up as a new task in the same context. A completed task cannot accept another message.

The rest of the surface

Method Use
GetTask One task’s current state, with optional history
ListTasks Your own tasks, filtered by context or state
CancelTask Stop a running investigation
SubscribeToTask Receive the task’s current snapshot, then follow newly committed updates
CreateTaskPushNotificationConfig, GetTaskPushNotificationConfig, ListTaskPushNotificationConfigs, DeleteTaskPushNotificationConfig Register a webhook to be told when a task changes, instead of staying attached

ListTasks and GetTask return only tasks created by the calling credential.

A push configuration may also ride in on SendMessage, in which case it is stored against the task that call creates. The callback URL is stored encrypted, and it is re-resolved before every delivery attempt — an address that resolves somewhere Anectico will not call is refused at delivery time, not only at registration time. Configuration IDs are opaque strings: supply one to preserve it, or omit it and let Anectico assign one. Deleting a configuration is idempotent and suppresses attempts that have not been sent yet, not only future ones.

The callback is also governed by the project’s current Sent outside Anectico content-policy decision. Anectico checks it immediately before each attempt, including retries. If the conclusion’s content is denied or the decision cannot be confirmed, the callback still receives task status and metadata but not the conclusion artifact or terminal answer text. A later policy change does not rewrite the durable task history inside Anectico.

Not offered

There is no authenticated extended card: everything this agent can do is on the public card, and GetExtendedAgentCard is refused with UnsupportedOperation.

When a stream ends early

A streaming call is authorized when you open it, and re-authorized while it runs. Anectico re-checks the credential against its authoritative record before handing over an update, so revoking a key, freezing it, or narrowing its scopes stops a SendStreamingMessage or a SubscribeToTask that is already in flight — within 30 seconds of the next update it would have delivered. The update that would have crossed that line is withheld, not delivered and then apologized for.

You learn about it as a final JSON-RPC error in place of the next update, and the code says what to do next:

Error What happened What to do
UNAUTHENTICATED The credential no longer authenticates Read the message. A credential that merely expired says to re-authenticate and call again; one that was taken away says to obtain a new one, and retrying with it will not work
UNAUTHORIZED The credential still authenticates but no longer holds what the stream was admitted with — a narrowed key, or a frozen one Do not retry with it; the answer will not change
SERVER_ERROR The credential could not be checked at all Transient. Back off and call again

The stream always ends on any of the three. Anectico will not carry on delivering while it is unsure the caller is still entitled to what it is delivering.

SubscribeToTask deserves a note, because it is the call with no natural end: it opens with the task’s current snapshot and then follows newly committed updates for as long as you stay attached. It is bounded by exactly the same re-check. If your stream ends and your credential is still good, simply re-subscribe: the opening snapshot gives you the converged current state before new updates resume.

Verify it works

curl -s https://app.anectico.com/.well-known/agent-card.json | jq '.skills[].id'

Then send one SendMessage with an identifier you can check by hand, and confirm the returned conclusion cites evidence you recognize. A 401 means the credential is missing or malformed; a 403 means the key lacks the scope to run investigations.

Next steps