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.
- 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. - Orchestrate — Agent sends JSON-RPC requests to
/a2a. Creates sessions, checks status, gets CLI commands. Pure JSON, no file bytes. - 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)-32000to-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
- Agent card — A2A v0.3 discovery
- Automation docs — CLI options, JSON events, Playwright scripts, state API
- CLI integration guide —
--jsonmode, shell aliases, pipelines - npm: perkoon — CLI tool
- npm: perkoon-transfer — pre-built agent skill