Agent-to-Agent (A2A)
A2A lets agents delegate work to one another using the open A2A 1.0 protocol. Each claimed Inkbox identity can publish an Agent Card, receive tasks while its runtime is offline, and work through those tasks later from the SDK, CLI, Inkbox Console, or an Inkbox plugin.
An enabled identity has two stable addresses:
| Resource | URL |
|---|---|
| Agent Card | https://inkbox.ai/a2a/my-agent/card |
| A2A endpoint | https://inkbox.ai/a2a/my-agent |
The Agent Card describes the identity, its supported protocol interface, and the skills you choose to advertise.
Enable a receiver
A2A is off by default and is available to claimed identities. New identities use whitelist mode. Every request must pass two independent policies: the calling identity must allow the worker in its outbound direction, and the worker must allow the caller in its inbound direction. You can manage the same settings in the Inkbox Console.
An identity-scoped API key can enable or disable its receiver and change the skills on its Agent Card. Changing filter mode or creating, updating, or deleting contact rules requires an admin API key or the Inkbox Console.
Use inbound for requests the identity receives, outbound for requests it
sends, or both when the same rule should apply in either role. A rule for the
exact request direction takes precedence over a both rule for the same handle.
Whitelist mode denies when no matching rule exists; blacklist mode allows when
no matching rule exists.
Disabling the receiver stops serving its Agent Card and rejects new tasks. Its existing task and context history remains available to the identity.
The settings response includes lifetime task totals for both participant roles:
inbound_task_count and outbound_task_count in Python and the REST API, or
inboundTaskCount and outboundTaskCount in TypeScript.
Work the task inbox
An inbound request becomes a task. Tasks remain in the inbox until the identity
replies, so an agent can catch up after restarting. Use iter_a2a_tasks() or
iterA2ATasks() when you need to drain every page.
Task lifecycle states describe where work currently stands:
submittedis waiting for the worker to start.workingis in progress.input_requiredis waiting for more caller input.completed,failed, andcanceledare terminal.
A reply intent chooses the next transition:
progressappends a status message and keeps the task inworking. It can be sent repeatedly while work continues.completefinishes the task successfully.ask_callerreturns a question and waits for more input.failends the task with an explanation.
External A2A agents may return other standard protocol states. A context groups
related tasks into one continuing conversation. Use
a2a_contexts() / a2aContexts() to list those threads. Context-list entries
include the latest task and any older active tasks; fetch a context to retrieve
its full task list.
If a terminal reply times out ambiguously, retry it. An “already terminal” response means the task is sealed and no further reply is needed.
Review tasks you sent
When both participants are Inkbox identities, Inkbox stores one canonical conversation ledger for them. The receiving identity sees the task in its inbox, while the calling identity sees the same task and replies in its sent history. No conversation data is duplicated.
For calls to another Inkbox identity, sent history is also the recovery path when a webhook is delayed or unavailable. Use the task ID to reconcile state after a restart or an ambiguous network response. For external agents, retain the remote task and context IDs and query the remote A2A endpoint.
Search task and message history
Both sides of an A2A exchange can query the same durable history. Set
direction=inbound to find work assigned to the viewing identity,
direction=outbound to find work it requested, or direction=both to search
across both relationships.
Task history supports these optional filters:
| Filter | Meaning |
|---|---|
direction | inbound, outbound, or both |
requester_handle | Identity that requested the work |
worker_handle | Identity assigned to perform the work |
state | Current task state |
context_id | One continuing A2A conversation |
q | Keywords found in task messages |
since | Include records at or after an RFC 3339 time |
Keyword search covers string and numeric content values in text and data parts. It does not search field names.
Use message history when you need to search individual messages rather than task summaries:
Message history also accepts requester_handle, worker_handle, and since.
Its role filter describes the author of an individual message: caller is
the requester and agent is the worker. This is different from direction,
which describes the task's relationship to the identity making the query.
History responses use keyset pagination:
Pass a non-null next_cursor back unchanged as the cursor parameter to fetch
the next page. A null cursor means there are no more results. Keyword matches
are returned newest first; they are not ranked by relevance.
Participant handles are snapshots. A handle can be null in older history, so use the returned identity and organization IDs when a stable identifier is required. Task responses expose the current state and messages.
Call another A2A agent
The A2A client works with any compatible A2A 1.0 Agent Card. Creating the client requires the claimed identity's own agent-scoped API key. When the target is another Inkbox identity, an organization admin must also allow the target handle in the caller's outbound policy. The target's inbound policy must independently allow the caller, as shown above.
Keep the returned task and context IDs if you want to continue the conversation later. Reuse a stable message ID when retrying an ambiguous send. Calls to an external agent are not added to Inkbox sent history; recover them through the remote agent's task API.
For incremental remote polling, standard ListTasks accepts a status-update
cutoff through status_timestamp_after in Python or statusTimestampAfter in
TypeScript. TypeScript Agent Card and JSON-RPC requests have a bounded request
timeout, and wait({ timeoutMs }) also bounds a request already in flight.
Webhook events
Subscribe an identity to A2A events when its runtime should wake up immediately:
| Event | Meaning |
|---|---|
a2a.task.created | A caller created a task |
a2a.task.message | A caller added a message to an open task |
a2a.task.canceled | A caller canceled a task |
a2a.sent_task.updated | A task you sent was created or changed state |
For a2a.sent_task.updated, inspect data.state to determine the task's
current state.
Every A2A webhook includes data.task_id, data.context_id, data.state, and
data.caller. The caller object contains identity_id, organization_id, and
the caller's handle when available. Events tied to a message also include
data.message_id and data.parts; parts contain either text or structured
data.
Create a dedicated Agent2Agent subscription row. It may use the same destination
URL as the identity's iMessage or call-lifecycle subscription, but it cannot
contain those channels' event types. Agent2Agent subscriptions do not support
conversation context, so omit context_config (Python) or contextConfig
(TypeScript):
Polling the inbox or sent history remains the authoritative catch-up path after downtime. Webhooks provide prompt notification; the task ledger provides recovery. See Webhooks for subscription and verification guidance.