Technical

A2A Protocol: Agent-to-Agent File Transfer with Perkoon

8 min read 295 views

Your agent framework discovers services, creates sessions, and orchestrates transfers — all through a standard JSON-RPC endpoint. No CLI parsing. No screen scraping. Just structured requests and structured responses.

Perkoon speaks A2A v0.3 — the open protocol for agent-to-agent communication, originally from Google, now under the Linux Foundation. If your agent framework supports A2A, it can discover and use Perkoon automatically.

How it works

Three steps. No accounts. No API keys.

  1. Discover — Agent reads the agent card at /.well-known/agent.json. Learns what Perkoon can do, what skills are available, and where the endpoint lives.
  2. Orchestrate — Agent sends JSON-RPC requests to /a2a. Creates sessions, checks status, gets CLI commands. Pure JSON, no file bytes.
  3. Transfer — Agent runs the CLI command. Files move P2P via WebRTC. Nothing touches the server.

The A2A endpoint is the orchestration layer. The CLI is the transfer layer. Clean separation. Your agent doesn't need to understand WebRTC, signaling, or ICE candidates. It just calls an API and runs a command.

Step 1: Discovery

curl https://perkoon.com/.well-known/agent.json

Returns an A2A v0.3 agent card:

{
"name": "Perkoon File Transfer",
"protocolVersion": "0.3.0",
"url": "https://perkoon.com/a2a",
"skills": [
{
  "id": "send-files-p2p",
  "name": "Send Files P2P",
  "description": "Create a P2P transfer session..."
},
{
  "id": "receive-files",
  "name": "Receive Files",
  "description": "Join an existing session as receiver..."
},
{
  "id": "session-status",
  "name": "Session Status",
  "description": "Check session state..."
}
],
"capabilities": {
"streaming": false,
"pushNotifications": false,
"multiTurn": false
}
}

The card tells your agent: here are the skills, here's the endpoint, here's what I accept. Standard A2A. Any compliant framework can parse this.

Step 2: Create a session

Send a message/send JSON-RPC request to /a2a:

curl -X POST https://perkoon.com/a2a \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
  "message": {
    "role": "user",
    "parts": [{
      "type": "data",
      "data": {"skill": "send-files-p2p", "name": "Q1 Report"}
    }]
  }
}
}'

Response:

{
"jsonrpc": "2.0",
"id": "1",
"result": {
"id": "K7MX4QPR9W2N",
"status": {"state": "completed"},
"artifacts": [{
  "artifactId": "session-info",
  "parts": [{
    "type": "data",
    "data": {
      "session_code": "K7MX4QPR9W2N",
      "sender_key": "a7f3...9e1b",
      "send_command": "perkoon send <file> --session K7MX4QPR9W2N --sender-key a7f3...9e1b",
      "receive_command": "perkoon receive K7MX4QPR9W2N",
      "share_url": "https://perkoon.com/K7MX4QPR9W2N",
      "expires_at": "2026-03-12T14:00:00Z"
    }
  }]
}]
}
}

You get everything you need: session code, sender key for authorization, the exact CLI command to run, and a share URL for the receiver. The sender_key prevents unauthorized senders from joining your session — only the session creator gets it.

Step 3: Transfer

Run the command from the response:

perkoon send report.pdf --session K7MX4QPR9W2N --sender-key a7f3...9e1b --json

The --session flag tells the CLI to join the session you already created via A2A instead of creating a new one. The --sender-key authorizes you as the sender. Add --json for structured event output your agent can parse.

The receiver runs:

perkoon receive K7MX4QPR9W2N

Or opens https://perkoon.com/K7MX4QPR9W2N in any browser. File moves directly between the two machines. P2P. Encrypted. No size limit.

Natural language

Don't want to construct DataParts? Send natural language instead:

{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"message": {
  "role": "user",
  "parts": [{"type": "text", "text": "I want to send a file"}]
}
}
}

The endpoint infers intent from keywords. “Send”, “transfer”, “create” → creates a session. “Receive from K7MX4QPR9W2N” → joins that session. “Check status of K7MX4QPR9W2N” → returns session state. DataParts are exact. TextParts are convenient. Both work.

Check session status

curl -X POST https://perkoon.com/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"id":"K7MX4QPR9W2N"}}'

Returns the session state: working (active), completed (ended), or input-required (paused). Task ID = session code. Standard A2A task lifecycle.

Security: sender keys

When you create a session via A2A (or the REST API), the response includes a sender_key. This is a random 256-bit secret that authorizes sender access to the session.

  • Senders must present the key when joining via --sender-key
  • Receivers never need it — they just need the session code
  • Browser users are unaffected — sender role is determined by the LiveView session

This prevents a rogue agent from joining your session as sender and uploading unwanted files. Knowledge of the 12-character session code lets you receive. The sender key lets you send. Separation of concerns.

Rate limits

The A2A endpoint shares rate limits with the REST API:

  • 10 session creates per minute per IP
  • 30 session joins per minute per IP

If you hit the limit, the response includes a Retry-After header. These limits exist to prevent abuse, not to slow down legitimate use. If your agent creates 10 sessions per minute, something else is wrong.

Error handling

Standard JSON-RPC 2.0 error codes:

  • -32600 — Invalid request (missing jsonrpc/method/id)
  • -32601 — Method not found
  • -32602 — Invalid params (unknown skill, bad session code)
  • -32000 to -32009 — Server errors (session not found, expired, full)

Every error includes a human-readable message field your agent can log or display. No guessing. No parsing HTML error pages.

When to use A2A vs CLI directly

Use A2A when your agent framework supports it, when you need programmatic session management, or when you want to create sessions on behalf of users and share the code later.

Use the CLI directly (perkoon send file.zip --json) when you just need to send a file right now and don't need to pre-create sessions. The CLI creates its own session and gives you the code. Simpler. Fewer moving parts.

Both paths end at the same place: a P2P WebRTC transfer with no file bytes touching the server. A2A adds orchestration. The CLI adds simplicity. Pick whichever fits.

Full reference

Frequently Asked Questions

Do I need an API key for A2A?

No. The A2A endpoint is open. No authentication, no API keys, no OAuth. Rate limits prevent abuse.

Can I use A2A without the CLI?

The A2A endpoint returns tokens and ICE servers. An advanced agent could use these to connect to the signaling WebSocket and establish WebRTC directly. But for most use cases, shelling out to the CLI is simpler and handles all the WebRTC complexity for you.

Does the A2A endpoint touch my files?

No. The endpoint only handles session orchestration — creating sessions, checking status, returning CLI commands. File bytes flow P2P between sender and receiver via WebRTC. The server never sees them.

What A2A methods are supported?

message/send (create/join sessions), tasks/get (check status). Streaming and push notifications are not supported — sessions are one-shot operations that complete immediately.

Topics

a2a file transfer a2a protocol agent to agent file transfer json-rpc file transfer perkoon a2a ai agent file sharing a2a v0.3

Ready to try P2P file transfer?

Send unlimited files for free. No signup required.

By starting, you agree to our Terms