SplitStreamdocs

Overview

SplitStream is a pay-per-use layer on Circle's Arc L1. Anyone can list a service — a piece of content (article, song, photo) or a paid API — and get paid per use in USDC, with the payment split across every contributor on their own chain. Buyers can be humans or AI agents; agents pay per call via the x402 standard.

You list a service →

Register content or an API endpoint with a price and a revenue split. Integrate nothing — we are the paywall.

Someone pays per use →

A human clicks, or an AI agent pays autonomously per call in USDC. The split settles to all owners across chains.

Core concepts

  • Piece — the unit you list. kind is article·photo·song·podcast (content) or api (a paid endpoint).
  • Split — contributors each take a share in basis points (must sum to 10000 = 100%); each is paid on their targetChain.
  • x402 — the HTTP "402 Payment Required" flow agents use to pay per call: request → 402 challenge → pay USDC → retry with proof → get result.
  • Credential injection — for authenticated APIs, you store the upstream key once; we inject it per call. The buyer gets access, never the key (no KYC).
For sellers — list a service

1. Get an API key

Open a publisher account once to get a scoped key (the demo key arc_test_sk_demo_0001 works out of the box).

sign up (tRPC) — or just use the demo key
curl -X POST localhost:8787/trpc/tenants.signup \
  -H 'content-type: application/json' \
  -d '{"name":"Acme Media","onchainAddress":"0xYourArcAddress0000000000000000000000000"}'
# → { apiKey: "arc_test_sk_...", ... }  (shown once)

2. List content (pay-per-piece, cross-chain split)

A reader pays a few cents to unlock; the price fans out to every contributor on their chain. Use the /publish form, or the API:

register an article with a 3-way split
curl -X POST localhost:8787/api/v1/pieces \
  -H 'content-type: application/json' -H 'x-api-key: arc_test_sk_demo_0001' \
  -d '{
    "title": "The Stablecoin Frontier",
    "kind": "article",
    "priceUSDC": "0.05",
    "contributors": [
      { "role":"writer",       "address":"0x1111…", "targetChain":"base",     "splitBps":6000 },
      { "role":"editor",       "address":"0x2222…", "targetChain":"arbitrum", "splitBps":2500 },
      { "role":"photographer", "address":"0x3333…", "targetChain":"ethereum", "splitBps":1500 }
    ]
  }'

It's now live in the catalog and shareable at /piece/<id>.

3. List a paid API (x402 pay-per-call)

Register any HTTP endpoint with a per-call price. You integrate nothing — SplitStream becomes the x402 paywall and proxies the call on payment.

register a paid API
curl -X POST localhost:8787/api/v1/pieces \
  -H 'content-type: application/json' -H 'x-api-key: arc_test_sk_demo_0001' \
  -d '{
    "title": "My App: FX Rates",
    "kind": "api",
    "priceUSDC": "0.01",
    "endpoint": "https://api.yourapp.com/v1/fx?from=USD&to=EUR",
    "httpMethod": "GET",
    "contributors": [{ "role":"owner", "address":"0xYourArcEvmAddress", "targetChain":"base", "splitBps":10000 }]
  }'

Multi-owner APIs split automatically — list several contributors.

4. Authenticated APIs — access without KYC

If your endpoint needs a key, store it once with the piece. It's write-only — never returned by any read endpoint — and injected per call. The paying agent gets the result, never the key.

add upstream auth: bearer · header · query
"auth": { "type":"bearer", "secret":"sk_live_…" }
"auth": { "type":"header", "name":"X-API-Key", "secret":"…" }
"auth": { "type":"query",  "name":"apikey",    "secret":"…" }

Reads expose only authenticated:true and authType — never the secret.

5. Getting paid — real USDC on Arc

  • Live (LIVE_X402) — the buyer's USDC payment is verified on Arc, then each owner is paid their split in real USDC on Arc. Proven end-to-end (owner balance 0 → 0.01 on-chain).
  • Mirror (default) — keyless demo: simulated settlement so anyone can try it instantly. Split math, credential injection, and live upstream calls are real either way.
  • Cross-chain — EVM contributors are paid on Arc directly; other chains route via Circle CCTP / Gateway.
For buyers & agents — consume a service

A. Install in your AI (MCP)

Add SplitStream's hosted MCP server to any MCP client (Claude Code, Cursor, …) by URL — no clone, no install. Your AI then has tools to browse and pay for the live catalog; the user just talks normally.

install once — remote, nothing to download
claude mcp add --transport http splitstream https://splitstream-api-production.up.railway.app/mcp

Other clients (Cursor, Windsurf, …): add an HTTP/Streamable MCP server pointing at https://splitstream-api-production.up.railway.app/mcp.

Tools the agent gains: list_pieces, call_api, pay_for_piece (+ treasury tools).

Local dev alternative (needs the repo + Bun): claude mcp add splitstream -- bun run apps/server/src/mcp/stdio.ts

then the human just asks — the AI discovers + pays
User:  "What's the USD→EUR rate right now?"
AI:    list_pieces → finds "FX Rate API"
       call_api(pieceId) → pays $0.01 → returns { "rates": { "EUR": 0.86 } }
AI:    "It's about €0.86 to the dollar."

B. x402 — any agent with a wallet

No SplitStream-specific install. An agent with an x402 client + a funded Arc wallet pays our endpoint the standard way. This is the same model the wider x402 ecosystem (incl. pay.sh) uses; Circle's pay-via-agent-wallet does exactly this on Arc.

the handshake (what an x402 client automates)
# 1. request the resource — no payment yet
POST /api/v1/pieces/<id>/call
→ 402 Payment Required
  { "x402Version":1, "accepts":[{
      "scheme":"exact", "network":"arc-testnet",
      "maxAmountRequired":"10000", "payTo":"0x…", "asset":"0x3600000000000000000000000000000000000000",
      "nonce":"0x…", "resource":"/api/v1/pieces/<id>/call" }] }

# 2. pay maxAmountRequired USDC to payTo on Arc (your wallet signs it)

# 3. retry with the proof
POST /api/v1/pieces/<id>/call
   X-PAYMENT: base64({ x402Version:1, scheme:"exact", network:"arc-testnet",
                       payload:{ nonce:"0x…", from:"0xYou", authorization:"<txHash>" } })
→ 200 OK
  X-PAYMENT-RESPONSE: base64({ success:true, transaction:"0x…", payer:"0xYou" })
  { "paid":true, "mode":"live-arc", "payments":[…], "upstream":{ "ok":true, "body":{…} } }

Try the full handshake locally: pnpm --filter @arcane/server x402:call. The single-use nonce blocks replay.

C. REST / curl (content unlock or known API)

unlock a content piece (public, no key)
curl -X POST localhost:8787/api/v1/pieces/<id>/pay \
  -H 'content-type: application/json' -d '{"payer":"reader"}'
# → { unlock: { contributors:[ …paid per chain… ] } }

D. Storefront (human, one click)

Open the storefront: browse pieces, hit Unlock (content) or Pay & call (API), and see the cross-chain fan-out + live result. No account. To pay with your own wallet in real USDC, add Arc first (below).

E. Add Arc Testnet to your wallet

To pay with your own wallet you need Arc Testnet added — most wallets don't have it yet. One click adds it. On Arc, USDC is the gas token, so you only ever need USDC (no separate gas coin).

Network nameArc Testnet
RPC URLhttps://rpc.testnet.arc.network
Chain ID5042002
Currency symbolUSDC
Block explorerhttps://testnet.arcscan.app
USDC (ERC-20)0x3600000000000000000000000000000000000000

The button uses your wallet's wallet_addEthereumChain; if it's unavailable, add the values above by hand. Need test USDC? Get it free from the Circle faucet (select Arc Testnet) — it covers both gas and purchases.

Reference

Endpoints

MethodPathWhat
POST/api/v1/piecesregister a piece (x-api-key)
GET/api/v1/piecesbrowse catalog
GET/api/v1/pieces/:idone piece
POST/api/v1/pieces/:id/payunlock content → split
POST/api/v1/pieces/:id/callx402 pay-per-call (API)
POST/api/v1/pieces/:id/claimclaim after a real wallet payment
ALL/mcpremote MCP (Streamable HTTP)

tRPC equivalents: pieces.list / get / create / unlock / callApi, traction.stats, agent.read. MCP tools: list_pieces, call_api, pay_for_piece.

x402 wire format

  • 402 body{ x402Version, accepts:[ PaymentRequirements ] }
  • PaymentRequirementsscheme:"exact", network:"arc-testnet", maxAmountRequired (atomic USDC), payTo, asset, nonce, resource, maxTimeoutSeconds
  • X-PAYMENT (request header, base64) — { x402Version, scheme, network, payload:{ nonce, from, authorization } }
  • X-PAYMENT-RESPONSE (response header, base64) — { success, transaction, network, payer }

Network & modes

  • Chain: Arc Testnet · id 5042002 · explorer testnet.arcscan.app
  • USDC (6-dp ERC-20): 0x3600000000000000000000000000000000000000 — also Arc's native gas token
  • Mirror mode (default): keyless, simulated settlement. LIVE_X402: real USDC on Arc (funded relayer).