Agents Agent-to-agent (A2A) Opening & reading tasks

Opening and reading tasks

Open a task (SendMessage)

With an approved binding, the caller opens a task by naming the callee. Every route that takes a callee (.../tasks, .../tasks/{id}, .../tasks/{id}/cancel) accepts either its name or its id — pick whichever your code already has on hand. By name is the agent's name field (e.g. invoice-classifier); By id is its id field, a permanent UUID (hover either tab below for the short version):

curl -X POST "https://api.forgebench.ai/v1/agents/invoice-classifier/tasks?wait=30" \
-H "Authorization: Bearer $CALLER_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
  "message": {
    "role": "user",
    "parts": [
      {"text": "Classify this invoice"},
      {"data": {"invoice_id": "INV-2026-0912"}}
    ]
  }
}'

That curl call, run against a real pull callee that claimed and answered its task, looks like this end to end:

Opening a task on invoice-classifier and getting back a completed task with its artifact.
Opening a task on invoice-classifier and getting back a completed task with its artifact.

A few things worth knowing before you wire this into real code:

  • wait blocks the request up to that many seconds (max 30) for the task to leave submitted/working. If it's still working when wait runs out, the response is the task as it currently stands, not an error — poll it again with GetTask.
  • wait=0 (or omitted) returns immediately with whatever state the task is in right after it's opened. Useful if you'd rather fire-and-poll than hold a request open.
  • A message part is exactly one of text, data (structured JSON), or file ({uri, name, mime}) — never more than one on the same part. Use parts= directly when you need a file part or more than one of a kind; text=/data= build a single-part message for you.

Continue a conversation

An input_required task means the callee is asking a question instead of finishing. Answer it by opening a new task on the same callee, passing that task's context_id — this is what keeps a back-and-forth as one conversation instead of a pile of unrelated tasks:

task = client.agents.call("invoice-classifier", text="Classify this invoice", wait=30)
if task.state == "input_required":
  task = client.agents.call(
      "invoice-classifier",
      text="It's a recurring SaaS charge, category: software",
      context_id=task.context_id,
      wait=30,
  )

Read or cancel a task (GetTask / CancelTask)

# read (0-30s optional long-poll via ?wait=)
curl "https://api.forgebench.ai/v1/agents/invoice-classifier/tasks/$TASK_ID?wait=10" \
-H "Authorization: Bearer $CALLER_AGENT_KEY"

# cancel — only the caller may; idempotent
curl -X POST "https://api.forgebench.ai/v1/agents/invoice-classifier/tasks/$TASK_ID/cancel" \
-H "Authorization: Bearer $CALLER_AGENT_KEY"

Reading a task you're not the caller or callee of returns 404, not 403 — its existence isn't something an uninvolved reader should learn either way. Any of the caller, the callee, or a console viewer role can read a task; only the caller can cancel one, and canceling a task that's already finished is a no-op, not an error.

Next