Agents - Agent2Agent protocol

Free Public A2A Agent Endpoint

Four task-shaped capabilities over Agent2Agent, revision 1.0, spoken as JSON-RPC 2.0. No account and no API key. This is a narrow surface on purpose, and the page says where the rest of the service lives.

  • No API key
  • Protocol 1.0
  • JSON-RPC 2.0
  • Four skills

Endpoint and card

POST/a2a

GET/.well-known/agent-card.json

https://aisenseapi.com

A third protocol, not a third copy

A2A is a protocol for one agent to hand work to another agent. MCP is a protocol for exposing tools to a model. They answer different questions, and this service does not pretend they answer the same one.

Most of what is here is tools. Hashing, encoding, UUIDs, time, QR codes and wallet generation are single calls that return an answer and are finished. A tool like that gains nothing from a task lifecycle, so it is not offered over A2A. It stays on REST, and on MCP where it is one of the twenty, with its arguments published in band so a client can read them before calling.

Four capabilities are different. In each of them the interesting object outlives the request: something waits, someone answers, a message arrives. That is what an A2A Task describes, and TASK_STATE_INPUT_REQUIRED is the human-in-the-loop pause written into the core protocol rather than bolted on. MCP needed an extension to say the same thing. So those four, and only those four, are here.

SurfaceWhat it is forWhat is reachable
MCPExposing tools to a modelAll twenty public workflow tools, with schemas
RESTDirect HTTP calls from any clientEverything the service publishes, including every read and wait path
A2ADelegating a durable task to an agentFour skills, all of which create something

MCP is the richer surface. The four A2A skills are four of the same twenty tools reached through a different protocol, so nothing on this page is exclusive to A2A, and nothing outside these four is reachable through it. If you want the service, connect over MCP.

The narrowness is also honest about what A2A can express here. The three read counterparts, read_agent_inbox, read_human_approval and read_webhook_capture, are not skills. A2A creates the resource and hands back its identifier. Reading it happens over REST or MCP, with the one exception of Agent Wake, whose state GetTask can report.

The agent card

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

The card is a plain GET, not a JSON-RPC method. The protocol names no method for fetching the public card, and this path is registered. A client that asks for /.well-known/agent.json is running something built before revision 0.3, when the name changed.

{
  "protocolVersion": "1.0",
  "name": "AI SENSE",
  "description": "Public workflow tasks for AI agents: ...",
  "version": "1.0.0",
  "documentationUrl": "https://aisense.no/free-public-apis",
  "preferredTransport": "JSONRPC",
  "supportedInterfaces": [
    {
      "url": "https://aisenseapi.com/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "extendedAgentCard": false
  },
  "defaultInputModes": [ "application/json", "text/plain" ],
  "defaultOutputModes": [ "application/json", "text/plain" ],
  "skills": [ ... ]
}
Card fieldWhat it declares
protocolVersion1.0, the protocol revision. The written specification this implementation follows is 1.0.1
versionThe version of this agent, which moves independently of the protocol
preferredTransportJSONRPC
supportedInterfacesEvery address this agent answers on. There is no top level url in 1.0, so read the URL from here
capabilitiesThree flags, all false. See the declined capabilities below
skillsFour entries, each with an id, a name, a description and tags

The three capability flags are written out as false rather than left off. In the specification a flag that is absent and a flag that is false have identical force, so the explicit value is for the reader: it says the choice was made rather than forgotten.

A skill entry carries no inputSchema and no callable address. That is not an omission on this card. The protocol has no such field, which is what the next section is about.

The card is served with Access-Control-Allow-Origin: *, an ETag and one hour of cache. Nothing on it changes without a deploy.

Naming a skill, which the protocol will not do for you

A2A skills are not addressable. There is no skill id anywhere on the wire, no inputSchema on a skill entry, and no parameter on SendMessage that names one. The card advertises four skills and then gives a caller no field in which to ask for one.

So this service defines a convention. Put the skill name and its arguments in a message part that carries structured data.

{ "skill": "agent-wake", "arguments": { ... } }

That object is a convention this service documents, not something A2A defines. A reader who goes looking for a standard field will not find one, and a different A2A agent will almost certainly have chosen a different convention. A data part is the normative way to carry structured content, which is why it is used here, but the two keys inside it are ours.

A worked call, creating a task that waits for a webhook:

curl -X POST https://aisenseapi.com/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "m1",
        "role": "ROLE_USER",
        "parts": [
          {
            "data": {
              "skill": "agent-wake",
              "arguments": { "event_type": "webhook", "timeout_seconds": 3600 }
            },
            "mediaType": "application/json"
          }
        ]
      }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "23d99254-ed8d-4361-b3bf-3a6cc5ee416d",
    "contextId": "23d99254-ed8d-4361-b3bf-3a6cc5ee416d",
    "status": {
      "state": "TASK_STATE_WORKING",
      "timestamp": "2026-09-06T17:10:52Z"
    }
  }
}

This agent has no language model and does not interpret free text. A message built only from text parts is refused, and the refusal carries the shape it wanted:

{"code":-32602,"message":"Name a skill in a data part, as {\"skill\":\"agent-wake\",\"arguments\":{}}. This agent has no language model and does not interpret free text."}

A skill name that is not one of the four is refused the same way, with Unknown skill. The card lists the ids this agent answers to.

The arguments object is the argument object of the matching MCP tool, unchanged. Each skill is carried out by the tool that already exists, so bounds, required fields and refusals are identical on both surfaces by construction. A bad argument comes back as -32602 carrying the tool's own message, for example Missing required string: title or event_type must be webhook, human or time. The argument reference is on each capability's own page, linked in the table below.

The four skills

Skill idWhat it createsReplyArguments
agent-wakeA durable task that stays open until a webhook arrives, a person answers or a chosen time passesTaskAgent Wake
human-approvalA hosted form for one person, or for a group of up to twenty with a separate link eachMessagecreate_human_approval
agent-inboxA mail address that lives at most 24 hours, with the verification codes and links pulled out of what arrivesMessageAgent Inbox
webhook-captureA URL that records the first request sent to it, with method, headers and bodyMessageWebhook Capture

Only agent-wake answers with a Task. The other three finish while the call is still open: the address, the form or the capture URL exists the moment they return, and there is no work in flight to track. So their reply is a Message carrying the created resource.

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "messageId": "msg-24a827bc3fde9ac5",
    "role": "ROLE_AGENT",
    "parts": [
      {
        "data": {
          "ok": true,
          "capture_id": "1ea9e754-1445-44f6-ab3c-aabaf0ac5123",
          "status": "pending",
          "update_url": "https://aisenseapi.com/services/v1/webhook_capture/1ea9e754-.../update",
          "read_url": "https://aisenseapi.com/services/v1/webhook_capture/1ea9e754-...",
          "wait_url": "https://aisenseapi.com/services/v1/webhook_capture/1ea9e754-.../wait/25",
          "expire_timestamp": 1788801052,
          "expire_datetime": "2026-09-07T17:10:52Z"
        },
        "mediaType": "application/json"
      }
    ]
  }
}

The data object is exactly what the matching MCP tool returns, so the field reference on that capability's own page applies without translation. Keep the identifier inside it. Nothing lists these resources, and GetTask cannot see them either: they are not tasks. Read them back through their REST or MCP paths.

Every identifier these skills return is a bearer secret. Anyone holding one reads the resource with no further check, so keep task ids, inbox ids and capture ids out of logs, transcripts and anything a model can quote back. Do not put credentials or personal data into a task that anyone with the id can read.

The Task and its states

agent-wake answers with a Task, and GetTask and CancelTask address it by id. Both accept id and, for clients that send it, taskId.

curl -X POST https://aisenseapi.com/a2a \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"GetTask","params":{"id":"23d99254-ed8d-4361-b3bf-3a6cc5ee416d"}}'
{
  "id": "23d99254-ed8d-4361-b3bf-3a6cc5ee416d",
  "contextId": "23d99254-ed8d-4361-b3bf-3a6cc5ee416d",
  "status": {
    "state": "TASK_STATE_WORKING",
    "timestamp": "2026-09-06T17:11:00Z"
  }
}
StateMeaning
TASK_STATE_WORKINGThe task is open and waiting for its event
TASK_STATE_INPUT_REQUIREDA person has to act before the task can go on
TASK_STATE_COMPLETEDThe event arrived. Terminal
TASK_STATE_FAILEDThe task expired or could not finish. Terminal
TASK_STATE_CANCELEDThe task was cancelled before its event. Terminal

It is CANCELED with one L. The protocol spells it that way and this service spells its own REST status cancelled. The two spellings are deliberate, not a typo on either side. Enum values travel as their SCREAMING_SNAKE names, JSON field names are camelCase, and timestamps are ISO 8601 in UTC with a trailing Z.

CancelTask cancels and then returns the task in its new state, so one call is enough to both act and confirm.

The A2A view of a task is deliberately thin: an id, a context id, a state and a timestamp. It does not carry the form URL of a waiting human event, and it does not carry the captured webhook body of a completed one. The id is the Agent Wake task id, so read those through the Agent Wake status path with the same value, where _meta holds formUrl and a completed task holds its result. There is no long poll on A2A. Poll GetTask, or use the REST wait path if you want one request held open.

A wrong task id and a task that never existed both answer -32001, with the same message. Nothing distinguishes them, because the id is the only credential there is and telling the two apart would turn the endpoint into a lookup oracle for valid ids.

Declined capabilities, and their two distinct codes

Seven of the protocol's methods are declined. They are not gaps and they are not planned work. Once a capability is declared false on the card, returning the specified error is the conforming behaviour, and every one of these is answered because the card says false a few lines above.

MethodCodeError
SendStreamingMessage-32004UnsupportedOperationError
SubscribeToTask-32004UnsupportedOperationError
GetExtendedAgentCard-32004UnsupportedOperationError
CreateTaskPushNotificationConfig-32003PushNotificationNotSupportedError
GetTaskPushNotificationConfig-32003PushNotificationNotSupportedError
ListTaskPushNotificationConfigs-32003PushNotificationNotSupportedError
DeleteTaskPushNotificationConfig-32003PushNotificationNotSupportedError

These are two families, not one. Do not map both codes onto a single "not supported" branch. -32004 says a transport or a card variant is not offered and you should poll instead. -32003 says this agent will never call you back, which changes how your own workflow has to be built. Collapsing them loses the difference at the exact moment it matters.

{"code":-32004,"message":"This agent does not declare the streaming capability. Poll GetTask instead."}
{"code":-32003,"message":"This agent does not declare the pushNotifications capability."}

The extended card is answered with -32004 and the message This agent publishes one public card and no extended card. There is nothing behind an authenticated card that the public one withholds, because there is no authentication.

Everything else that can go wrong uses the ordinary JSON-RPC codes:

CodeWhen
-32700The body is not JSON. Answered as HTTP 200 with an id of null, since the id is genuinely unknown
-32600jsonrpc is not "2.0", or the request is not a well formed request object
-32601The method is not one the protocol names
-32602No message, no skill named, an unknown skill, or a bad argument
-32001The task id names nothing this agent can read
-32603The skill could not complete. Operational detail is not returned

Every one of these arrives as HTTP 200 with the error inside the JSON-RPC envelope, which is what a JSON-RPC client expects. The transport level failures are in the limits table below.

Why ListTasks is always empty

{"tasks":[],"nextPageToken":"","pageSize":50,"totalSize":0}

ListTasks is implemented, answers successfully, and returns nothing. That is the intended behaviour and it will not change while the endpoint is open.

The specification requires every operation to scope its results to the calling client's authorized access boundaries, and requires this method in particular to return only the tasks visible to the authenticated client. This service authenticates nobody. There is no account, no API key and no principal to scope to. A list that ignored the requirement would hand every caller every task id on the service, and those ids are the only credential the tasks have. An empty page is the conservative reading and it leaks nothing.

All four fields are returned rather than only tasks and nextPageToken, because the protocol requires all four.

The practical consequence: keep your own task ids. Write them into your job state at the moment SendMessage returns. Nothing on this endpoint, and nothing on any other surface, will give a lost id back to you.

Limits

LimitValue
MethodPOST, plus OPTIONS for a preflight. Anything else answers HTTP 405 with Use POST for JSON-RPC
Request body256 KB. A larger body answers HTTP 413
BatchesSupported. Send an array of request objects and receive an array of replies
NotificationsA request with no id is answered with HTTP 204 and no body, and nothing is carried out: the request is dropped before any skill runs
SkillsFour
Task lifetime24 hours maximum, and 60 seconds minimum for a wake task
Streaming and pushNot offered. Poll GetTask
AuthenticationNone. The card declares no security scheme

A batch is answered in order, notifications inside it produce no entry, and a batch made entirely of notifications gets HTTP 204 and no body at all.

Each skill keeps whatever caps its own capability documents, and they count the same client whichever protocol created the resource. New inboxes, for instance, are capped per client per UTC day whether the request came in over REST, MCP or A2A, because the same tool does the work. The per capability pages carry the numbers.

Everything created here lasts at most 24 hours and is then removed. Nothing is recoverable afterwards, and no call restores an expired resource.