The Complete Guide to Agent-to-Agent Marketplaces in 2026
By a senior engineer building autonomous AI agents
Introduction
Agent‑to‑agent (A2A) marketplaces have become the plumbing that lets one autonomous service discover, negotiate, and pay another for a discrete capability—think “micro‑service as a service”. By 2026 the ecosystem is mature enough that developers can treat another agent as a library call, but the underlying mechanics are still distributed, trust‑minimized, and cost‑sensitive. This guide walks through the architectural pieces you need to stitch together, shows concrete code for a minimal buyer and seller, and calls out the trade‑offs you’ll encounter in production.
Core Concepts
| Term | Meaning (2026) | Typical Implementation |
|---|---|---|
| Agent | A long‑running process that exposes a deterministic API (usually JSON‑over‑HTTP or gRPC) and can hold cryptographic keys for signing/payment. | Built with any language; often wrapped in a lightweight runtime (e.g., WasmEdge, Micrium). |
| Marketplace | A discovery + settlement layer that does not host the agent’s logic. It maintains an index of capabilities, verifies signatures, and escrows payment tokens. | Usually a set of smart contracts on a low‑fee L2 (Base, Arbitrum Nova) plus an off‑chain gossip/DHT for metadata. |
| Capability Descriptor | A machine‑readable schema (JSON‑Schema + OpenAPI‑like extensions) that lists input/output types, required auth, pricing, and SLAs. | Stored on IPFS/Filecoin; hash referenced in the on‑chain registry. |
| Settlement Token | The unit of value used for payment; in most A2A markets today it’s a stablecoin (USDC) on an L2 to keep gas < $0.001. | ERC‑20 contract; approval‑and‑transfer pattern. |
| Reputation | A lightweight score derived from on‑chain success/failure events and off‑chain attestations. | Stored in a separate contract; queried before committing funds. |
High‑Level Flow
-
Register – Seller publishes a capability descriptor to IPFS, then calls
Marketplace.register(bytes32 ipfsHash, uint256 pricePerCall, address token). - Discover – Buyer queries the marketplace’s off‑chain index (GraphQL or plain HTTP) for descriptors matching a keyword or input/output type.
- Quote – Buyer reads the on‑chain price, optionally asks the seller for a signed nonce‑based quote to avoid front‑running.
-
Escrow & Call – Buyer approves the marketplace to pull the required token amount, then invokes
Marketplace.execute(bytes32 ipfsHash, bytes calldata input). The contract:- Verifies the seller’s signature on the descriptor.
- Escrows funds.
- Calls the seller’s agent via its registered HTTP/gRPC endpoint (using a trusted oracle or relayer).
- On successful response, releases funds to the seller; on timeout or error, funds are returned (minus a small dispute fee).
-
Feedback – Both parties can submit a signed receipt (
success/failure) that updates reputation scores.
Minimal Working Example
Below is a buyer written in Python (3.11+) that interacts with a mocked marketplace contract via Web3.py. The seller side is a simple Flask endpoint that echoes back a string and signs the response with an ECDSA key (secp256k1).
Note: This code is intentionally stripped down for clarity. Production systems need proper key management, retry logic, circuit breakers, and gas estimation.
1. Seller (agent)
# seller.py
import os
import json
from eth_account import Account
from eth_account.messages import encode_defunct
from flask import Flask, request, jsonify
app = Flask(__name__)
# In practice load from a vault or HSM
PRIVATE_KEY = os.getenv("SELLER_PRIV") # hex string
ACCOUNT = Account.from_key(PRIVATE_KEY)
def sign_payload(payload: dict) -> str:
"""Return a hex ECDSA signature of JSON‑canonicalized payload."""
message = json.dumps(payload, sort_keys=True).encode()
encoded = encode_defunct(text=message.decode())
signed = ACCOUNT.sign_message(encoded)
return signed.signature.hex()
@app.route("/echo", methods=["POST"])
def echo():
data = request.get_json(force=True)
# Very simple capability: return the same string upper‑cased
result = {"output": data.get("input", "").upper()}
# Attach a signature so the buyer can verify authenticity
result["signature"] = sign_payload(result)
return jsonify(result)
if __name__ == "__main__":
# Listen on localhost for demo; in prod expose via TLS + auth
app.run(host="0.0.0.0", port=8080)
2. Buyer
python
# buyer.py
import os
import json
import time
import requests
from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_defunct
# ---- Configuration -------------------------------------------------
RPC_URL = os.getenv("BASE_RPC") # e.g. https://base.mainnet.rpc.dev
MARKETPLACE_ADDR = Web3.to_checksum_address(os.getenv("MPL_ADDR"))
SELLER_ENDPOINT = os.getenv("SELLER_URL") # http://seller:8080/echo
BUYER_PRIV = os.getenv("BUYER_PRIV") # hex string
USDC_ADDR = Web3.to_checksum_address(os.getenv("USDC_ADDR")) # Base USDC
# -------------------------------------------------------------------
w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = Account.from_key(BUYER_PRIV)
usdc_abi = [{"constant":False,"inputs":[{"name":"spender","type":"address"},
{"name":"value","type":"uint256"}],
"name":"approve","outputs":[{"name":"","type":"bool"}],
"type":"function"},
{"constant":True,"inputs":[{"name":"owner","type":"address"},
{"name":"spender","type":"address"}],
"name":"allowance","outputs":[{"name":"","type":"uint256"}],
"type":"function"}]
usdc = w3.eth.contract(address=USDC_ADDR, abi=usdc_abi)
# Minimal marketplace ABI (only register/execute needed)
mpl_abi = [
{"inputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},
{"internalType":"uint256","name":"price","type":"uint256"},
{"internalType":"address","name":"token","type":"address"}],
"name":"register","outputs":[],"type":"function"},
{"inputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},
{"internalType":"bytes","name":"input","type":"bytes"}],
"name":"execute","outputs":[{"internalType":"bytes","name":"output","type":"bytes"}],
"stateMutability":"payable","type":"function"}
]
marketplace = w3.eth.contract(address=MARKETPLACE_ADDR, abi=mpl_abi)
def approve_usdc(amount_wei: int):
"""Allow marketplace to pull USDC from buyer."""
txn = usdc.functions.approve(MARKETPLACE_ADDR, amount_wei).build_transaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
"gas": 100_000,
"maxFeePerGas": w3.to_wei(2, "gwei"),
"maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
"chainId": w3.eth.chain_id,
})
signed = account.sign_transaction(txn)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Approved {amount_wei/1e6} USDC")
def call_marketplace(ipfs_hash: str, payload: dict):
"""Escrow payment, invoke seller, return verified output."""
# 1. Determine price (here we hard‑code; in reality read from registry)
price_wei = w3.to_wei(0.05, "ether") # $0.05 worth of USDC (1 USDC ≈ 1e-6 ETH on Base)
approve_usdc(int(price_wei * 1.1)) # slight over‑approve for safety
# 2. Execute via marketplace
input_bytes = json.dumps(payload).encode()
txn = marketplace.functions.execute(
Web3.to_bytes(hexstr=ipfs_hash),
input_bytes
).build_transaction({
"from": account.address,
"value": 0, # payment handled via ERC20 transfer inside contract
"gas": 250_000,
"maxFeePerGas": w3.to_wei(2, "gwei"),
"maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
"chainId": w3.eth.chain_id,
})
signed = account.sign_transaction(txn)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
# 3. Decode output
output_bytes = marketplace.functions.execute(
Web3.to_bytes(hexstr=ipfs_hash),
input_bytes
).call()
result = json.loads(output_bytes.decode())
# 4. Verify seller signature (optional but recommended)
sig_hex = result.pop("signature")
msg = encode_defunct(text=json.dumps(result, sort_keys=True))
recovered = Account.recover_message(msg, signature=bytes.fromhex(sig_hex))
assert recovered.lower() == ACCOUNT.address.lower(), "Bad signature"
return result
if __name__ == "__main__":
# Example: seller has already registered;
Top comments (0)