Paid-inbound layer for AI agents. Toll paid on-chain via x402, negotiated over AXL, priced in ENS.
Tollgate is Stripe for agent-to-agent calls — a paid-inbound layer that makes AI voice agents pay each other a micro-toll before opening a negotiation channel.
Every business is about to deploy a voice agent. The moment that's true, agent-to-agent spam becomes the next robocall crisis: LLM-generated calls cost a fraction of a cent to send and ten cents to listen to, and there is no postage, rate-limiting, or Stripe-for-agents in existence. Tollgate is the toll booth.
A human asks their agent, "Book me a table at Bella for Friday." The caller agent (Alex) resolves bella.eth via ENS to discover Bella's AXL node, toll price, and capabilities. Alex's agent dials Bella over Daily/Pipecat voice. Both sides detect they are agent-to-agent. Alex's agent invokes KeeperHub's MCP server to pay a $0.25 USDC toll over x402, KeeperHub returns a signed receipt and tx hash, and an encrypted AXL P2P channel opens — audible as a modem-sweep handshake on the voice line. The agents exchange structured PROPOSE → ACCEPT → CONFIRM messages over AXL, sonified live in the browser as R2-D2-style chirps. KeeperHub then executes the booking deposit. Both agents drop back to voice and report to their humans. Roughly 30 seconds total.
Tollgate isn't a hardcoded two-agent demo. We ship a real onboarding flow at /create-agent: a user signs in with Privy, picks their capabilities (booking, quotes, support, orders, scheduling, payments), sets their toll price and currency, and our backend creates a real ENS subdomain under spokenagents.eth on Sepolia in two transactions — setSubnodeRecord to mint the subnode and setText for each record (axl.node, contact.price, contact.wallet, contact.workflow, capabilities, agent.version). Both steps are idempotent: re-registering the same agent skips minting, and updating one record skips the others. The new agent is mirrored into a local JSON registry that supports parallel resolution and capability-based discovery, so a caller can ask "find me an agent that does dining" and dial whoever is cheapest. The real-world claim is concrete: deploy an agent in under a minute, get a globally discoverable on-chain identity with public pricing, and start accepting paid inbound calls.
A FastAPI call-control backend that spawns Pipecat voice pipelines (Deepgram STT, Gemini, Deepgram TTS), a four-phase negotiation orchestrator, an AXL session client over the Gensyn Go binary's HTTP bridge, a KeeperHub MCP client doing real execute_transfer calls with SSE polling, on-chain ENS subdomain registration via web3.py, a /create-agent wizard with Privy wallet auth, and a live WebSocket trace UI that visualizes ENS lookup → toll tx → AXL messages → settlement.
The backend is Python 3.11 with FastAPI for call control. Each call spawns a Pipecat caller-agent subprocess wired as DailyTransport → Deepgram STT → Google Gemini → Deepgram TTS → BeatInjector, while a long-lived callee process handles inbound. A four-phase negotiation orchestrator drives ENS lookup → KeeperHub toll → AXL handshake → KeeperHub settlement and broadcasts structured events to a WebSocket bus on port 8765 that the browser canvas consumes live. The KeeperHub client speaks JSON-RPC against the MCP server at app.keeperhub.com/mcp and handles both application/json and text/event-stream responses (the MCP HTTP transport spec requires advertising both, even for sync tool calls), with a two-step initialize plus notifications/initialized handshake gated behind an asyncio lock to prevent partial-init races. Toll payments fire execute_transfer for USDC on Sepolia, then poll get_direct_execution_status with exponential backoff (1.5s growing to 5s) until a transactionHash lands — the initial response can return status=completed without a tx hash, so we always poll at least once. AXL has no Python SDK, so AXLSession talks to the Gensyn Go binary's local HTTP bridge directly: GET /topology for peer discovery (64-character hex ed25519 pubkey), POST /send with X-Destination-Peer-Id, and a 0.3s GET /recv poll loop returning 200 with a payload or 204 if empty. ENS records are read with web3.py and written via setSubnodeRecord plus idempotent setText calls under spokenagents.eth, with EIP-1559 gas estimation tuned for Sepolia's tiny base fee. The hacky bit worth calling out: AXL message traffic is sonified live on the voice line as R2-D2 chirps. BeatInjector is a custom Pipecat FrameProcessor that pushes one raw-PCM OutputAudioRawFrame per non-whitespace character, with each character mapped deterministically to a frequency between 200 and 3800 Hz via MD5, sine wave outbound and square wave inbound — so "PROPOSE" plays as seven distinct rapid tones synced to the actual AXL send. The audio is theater, not transport (the real bytes are on AXL), but it is synced to real events, which is what makes the demo land. The frontend is React 18 with Vite, Tailwind v4, and Framer Motion, using the Daily JS SDK for the WebRTC voice channel and a TracePanel that consumes the WebSocket event bus to render the live ENS-resolve → toll-tx → AXL-message → settlement timeline alongside per-participant audio visualizers driven by the Web Audio API.
ENS is the directory and the onboarding rail — without ENS there is no discoverable pricing and no way for new agents to join. KeeperHub is the payment rail — every channel open is an x402 transaction with a real signed receipt and audit trail. Gensyn AXL is the negotiation transport — two separate nodes carrying a full multi-round PROPOSE → ACCEPT → CONFIRM exchange with no central broker.

