Streamlivr

Pay-per-request access to public creator, brand, streams, content and music data over x402 on Celo.

Hosted MCP Server

npx add-mcp 'https://api.streamlivr.com/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Streamlivr: agents buy creator data on Celo

Streamlivr is a live-streaming and music app. Creators publish profiles, posts and tracks. This repository is the payment layer that sells that public data to software, one HTTP request at a time, over x402 on Celo. Money that arrives gets attributed to the creators whose data was served, and payouts leave from the Celo wallet the settlements landed in.

Five paid routes: one cent per credited creator on data pages, with a one-cent minimum; ping and single-profile requests cost one cent. Payments settle in USDC through the hosted Celo facilitator. The buyer signs an EIP-3009 authorization off-chain, retries the request, and the facilitator pays the gas. A buyer never needs CELO.

The production service runs inside Streamlivr's private repositories. This repo carries the same payment modules, a runnable reference server, and a browser demo site that pays real mainnet invoices.

Submitted to the Celo Agents at Work hackathon. Primary track: real-world adoption. Additional track: Judges' Favorite.

Start here: the demo site

demo/web is a Next.js checkout that pays the live API from a burner wallet in your browser. One click runs the whole loop: quote, signature, settlement, and a transaction link you can open on Celo Blockscout. This is the fastest way to see the system work.

cd demo/web/web
cp .env.example .env.local

Set five values in .env.local:

NEXT_PUBLIC_API_BASE_URL=https://api.streamlivr.com
NEXT_PUBLIC_BURNER_PRIVATE_KEY=0x...        # a throwaway key you fund
NEXT_PUBLIC_ENABLE_MAINNET=true             # false keeps the page on Celo Sepolia
NEXT_PUBLIC_MAX_ATOMIC_PER_REQUEST=1000000  # $1.00, checked before anything is signed
NEXT_PUBLIC_MAX_ATOMIC_PER_SESSION=5000000  # $5.00 for one browser session
npm install
npm run dev        # http://localhost:3008

The page opens on Celo mainnet. The suggestion chips are read live from the seller's free inventory route, so they name tags and countries that are actually in the catalogue.

You can also type a sentence. "who is making amapiano in Lagos" and "what does a page cost" go to POST /api/v1/agent/chat/interpret, a free route that reads the sentence with the same language model the Streamlivr app uses for its support chat. The route returns one call, the page makes it with the burner wallet, and the conversation stays in the browser. The model key stays on the server. Run the demo without one and the page reads the sentence itself, so the chat works either way.

The chat first probes the route and shows the invoice it got back, then signs the authorization in the background and repeats the call with the payment header. Nothing pops up, because the burner key lives in the bundle. The reply arrives with the settlement transaction hash, and Payout ledger in the header shows the same payment split 60/40 between the creators and the platform. On chain that settlement is one USDC transfer to the platform wallet. The 60/40 split is recorded in the ledger against every creator the page credited, and those balances leave in batches once they clear the payout minimum.

Funding the burner. The key ships inside the browser bundle, so treat it as public and keep it small. Send Celo mainnet USDC to the address printed at the top of .env.local; $0.05 covers a long session at these prices. Do not send CELO, because the facilitator pays the transaction fee. The per-request and per-session caps are the actual protection, and they are enforced before a signature is created.

If a payment fails, it is almost always one of three things: the wallet is empty, the API base URL points somewhere that does not serve these routes, or the API does not allow the page's origin through CORS. The page shows the raw headers and the error from each step, so you can see which.

The reference server

demo/server.ts is the same flow with an in-memory store instead of a database. Read it if you want to see how the seller side is wired: route config, the facilitator client, settlement, replay protection and the 60/40 split. The store is generated to production scale from a fixed seed: 2,400 creators, 6,000 posts, 5,000 tracks. That makes the search, the cursors and the "50 of 2,431" summaries are exercised against a dataset the size of the real one. It is deliberately small enough to read in one sitting.

cp .env.example .env
# set X402_API_KEY and X402_PAY_TO, leave X402_NETWORK=testnet for a first run
npm install
npm test           # 135 unit tests, no network access needed
npm run demo       # seller on http://127.0.0.1:3000
RoutePriceReturnsAttribution
GET /api/v1/agent/ping10000Liveness check that proves settlement worksnone
GET /api/v1/agent/listings10000 eachPublic creator listings, searchable and pagedevery creator on the page
GET /api/v1/agent/posts10000 eachPublic posts: captions, tags, media, engagementthe owner of the audio on each post
GET /api/v1/agent/catalog10000 eachCatalog metadata with the owner of each recordingthe artist who owns each recording
GET /api/v1/agent/creator/:id10000One public profile with their public countsthat creator
GET /api/v1/agent/pricingfreeThe price rule, the asset and worked examplesnone
POST /api/v1/agent/chat/interpretfreeTurns a visitor sentence into one callnone
GET /api/v1/agent/statsfreeHow many creators, posts and tracks there are, and the busiest tagsnone
GET /.well-known/agent.jsonfreeA2A agent card with prices and the ERC-8004 identitynone
GET /.well-known/mcp.jsonfreeMCP server card listing the same routes as toolsnone
GET /openapi.jsonfreeOpenAPI 3.1 document for every route, with x402 pricing per operationnone
POST /mcppaid per toolMCP JSON-RPC, same prices as the HTTP routessame as the route called
GET /api/v1/agent/reputationfreeOn-chain ERC-8004 reputation plus settled volumenone
GET /healthzfreeProcess healthnone
GET /demo/settlementsfreeWhat this process settled, with the splitnone
GET /api/v1/agent/demo/settlementsfreeThe same ledger in the shape the demo site rendersnone
GET /api/v1/agent/demo/creatorsfreePer-creator balances for the demo sitenone

Every public row, one page at a time

The three data routes carry ?q=, ?limit= and ?cursor=. A request costs one cent per creator it credits, with a one cent minimum, so the price follows the page rather than the route. A page that credits three creators costs 30000, a page that credits fifty costs 500000, and the ceiling is 2000000 for the two hundred creators the routes will credit in one request. A page that matches nothing costs nothing.

These are composable queries over the public data, not a fixed set of search phrases. Listings also accept verified=true|false, minFollowers=<number> and sort=followers|newest. Posts accept sort=newest|views. Listings default to most followed first, posts to newest first, and the catalog to title order. The same filters, sort and limit must be sent with nextCursor.

For example, top 3 creators becomes GET /api/v1/agent/listings?limit=3&sort=followers. top 3 verified fashion creators in Nigeria with at least 1000 followers becomes GET /api/v1/agent/listings?q=fashion+nigeria&limit=3&verified=true&minFollowers=1000&sort=followers. top 3 posts in Nigeria uses GET /api/v1/agent/posts?q=nigeria&limit=3&sort=views. The catalog has no public popularity ranking, so the demo does not sell an alphabetical page as "top tracks."

That is why limit is the cost control and why the seller prices the invoice from the page it is about to serve. The quote and the rows cannot disagree: src/x402/pagePlan.ts resolves the page once, the 402 is priced from it, and the 200 serves that same page.

What a row pays. The credits follow the work, not the account that published it.

The rowWho is credited
A post with the publisher's own recordingthe publisher
A post carrying another creator's soundthe creator who owns the sound
A post whose audio is a commercial recordingthe artist, when that artist has a Streamlivr account
Audio nobody on Streamlivr ownsnobody; a page with no credited creator is free
A catalog recording owned by a creatorthe owner
A catalog recording with no owner herenobody; a page with no credited creator is free

Every post and catalog row says which of these it is, in a label block and an attribution note the buyer can read without knowing the rule. Using a track is not owning it, so the creators whose posts drew on a recording are never the people that recording pays. src/x402/ownership.ts is the rule, in one file, with tests, and both the production API and the reference server call it.

q matches the fields that route actually holds. Listings match username, display name, bio, country (code or name), and the hashtags on that creator's public posts, so "amapiano creators" reaches the people behind the tag rather than accounts with the word in a name. Posts match title, description, hashtag and the publishing creator's country. Catalog matches title, artist and ISRC.

A sentence is read the way a person means it. Filler words and the noun for the dataset are dropped, so "show me creators in Nigeria" searches one country name rather than six words that have to appear in a username, and "lagos producer" still narrows to both words. If the strict reading matches nothing, the route tries the same words as an either-one-is-enough match before it gives up.

A page with no rows is not charged. The route answers 200 with an empty page and x-no-charge: empty-page, and the signed authorization is never submitted, so a search that misses costs the buyer nothing.

curl "http://127.0.0.1:3000/api/v1/agent/listings?q=lagos&limit=25"
{
  "creators": [{ "id": "cr_01310", "username": "velvet.lagos", "countryCode": "NG" }],
  "page": { "total": 275, "limit": 25, "returned": 25, "hasMore": true, "nextCursor": "eyJmb2xsb3dlckNvdW50Ijoi…" },
  "query": { "q": "lagos", "limit": 25 }
}
  • page.total is how many rows matched, not how many came back, so a buyer can see the size of the dataset without paying for all of it.
  • page.nextCursor is a keyset cursor, not an offset: it carries the sort values of the last row served, so a page stays correct while rows are inserted underneath it and a stale cursor skips forward instead of repeating rows. src/x402/paging.ts is the whole implementation, and it is unit tested.
  • q is tokenised, and a token has to match a field for its reading to count. The first reading drops the words that carry no data ("show me", "creators", "in") and requires the rest, which is what makes "music nate dogg" find Music & Me by Nate Dogg instead of returning everything containing "music". A reading that matches nothing hands over to one that accepts any remaining word, and after that the page is empty and free.
  • limit is clamped to 200 rather than rejected, so a client that asks for too much still gets an answer it can use.

The dataset is what the app already shows anyone: public accounts, and posts that are published and public, with the creators behind them. A creator who revokes agent access is removed from all three routes. The profile route answers 404 and their rows disappear from listings, posts and the catalog. Emails, phone numbers, wallet addresses, KYC state and private or followers-only content are never selected, at any price. src/x402/access.ts is the rule, in one file, with tests.

Amounts are strings in token base units. Every supported asset uses 6 decimals, so 10000 is one cent.

Ask for a paid route without paying, and the terms come back in the payment-required header, base64 encoded as x402 v2 specifies. x402 v2 leaves the body empty, which clients and validators that read JSON see as an invoice with nothing in it, so the server repeats the same object as the JSON body:

curl -s http://127.0.0.1:3000/api/v1/agent/catalog | jq
# or decode the header, which carries the identical object:
curl -sD - -o /dev/null http://127.0.0.1:3000/api/v1/agent/catalog \
  | awk 'tolower($1) == "payment-required:" { print $2 }' \
  | base64 -d | jq
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.streamlivr.com/api/v1/agent/catalog",
    "serviceName": "streamlivr-catalog",
    "tags": ["x402", "celo", "music", "catalog"],
    "iconUrl": "https://streamlivr.com/assets/STREAMLIVR%20ICON%20(1).png"
  },
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:42220",
    "maxTimeoutSeconds": 120,
    "payTo": "0xYourCeloWallet",
    "amount": "10000",
    "asset": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
    "extra": { "name": "USDC", "version": "2", "assetTransferMethod": "eip3009" }
  }]
}

resource.url is the URL that was requested, query string included, so the invoice names the same page its amount was priced from.

The extra block is the EIP-712 domain the buyer signs over. Get name or version wrong and the facilitator refuses the payment, which is why those values live in src/x402/config.ts instead of being typed at the call site. USDC reports version 2, USDT and USAT report 1.

Set X402_ENABLED=false and the data routes answer unpaid, so the search and pagination contract can be read against a running seller without a facilitator key. That is what npm run demo does by default: it generates 2,400 creators, 6,000 posts and 5,000 tracks at boot, and the bundled chat pages through them for free. The two well-known discovery documents still resolve in that mode, with an empty route list and paymentEnabled: false; /mcp and the reputation read return 404, because neither can do anything useful without settlement.

What one payment does

  1. An agent asks for a paid route with no payment and gets 402 plus the invoice in the payment-required header. The amount is one cent per creator that request would credit.
  2. It signs an EIP-3009 TransferWithAuthorization off-chain. No approval transaction, no gas.
  3. It repeats the request with the payment-signature header.
  4. The seller asks the Celo facilitator to verify the signature, then to settle it.
  5. USDC moves from the buyer to X402_PAY_TO inside the token contract, in one transfer. The facilitator never holds the funds.
  6. The seller stores the settlement and splits 60% to the creators the page credited and 40% to the platform, one ledger row per credited creator, then replies with the data plus a payment-response header holding the transaction hash.

One on-chain transfer, many ledger rows. A page that credits fifty creators is one USDC transfer to the platform wallet and fifty balances the payout job settles later, each of them a whole cent before the split.

Repeat the same signed payload and the stored response comes back instead of a second charge. The authorization nonce is single-use, so a second settlement would fail anyway.

Where creators get paid

Creators hold a Celo wallet inside the Streamlivr app, on mainnet and Sepolia, holding USDC, USDT and USDm. Signing goes through a Web3Auth-backed provider, so key material stays on the device.

A payout only goes to an address the creator proved they control. The API issues a nonce-bound message, the creator signs it, and the API recovers the signer and stores the checksummed result. One challenge, one use, ten minutes to expire.

Money moves in three steps, never one. A batch is prepared as PENDING, approved as a separate act, and only then broadcast. Every step needs SUPER_ADMIN and writes an audit row.

PENDING -> APPROVED -> SUBMITTED -> CONFIRMED
                              \-> FAILED, which releases the balance for a later run

Four gates decide who is payable, read at prepare time and again at send time:

GateSourceBlocks when
IdentityUser.kycStatusanything other than VERIFIED
Fraud suspensionUser.isFraudSuspendedtrue
Fraud scoreUser.fraudRiskScore80 or higher
Payout walletprimary Celo walletmissing

kycStatus is not a field an operator edits. It holds the answer from the identity provider, written by a signed webhook and refreshed by a reconcile worker. The payout path reads that stored answer rather than calling the provider inline, so a slow provider cannot hold up a treasury transfer.

Two rules keep the treasury honest. The transaction hash is stored before the receipt is awaited, so a confirmation timeout leaves the row SUBMITTED for reconcile rather than re-sending live money. Execution re-checks the wallet, so a creator who changes address between approval and send stops the payout instead of redirecting it.

Agent discovery and reputation

An agent that has never seen this project can go from a domain name to a paid call without a human reading these docs.

GET /.well-known/agent.json is an A2A agent card. Skills map onto the paid routes and carry their price in the tags. Payment detail sits in two declared capability extensions: one for x402 (scheme, network, asset, receiving wallet, facilitator, price per route) and one for ERC-8004 pointing at the registries, the agent id and the reputation endpoint.

GET /.well-known/mcp.json describes the MCP server: transport, protocol version, the payment metadata key x402/payment, and one tool per route. POST /mcp is a stateless Streamable HTTP handler that answers initialize, tools/list, tools/call and ping.

curl -s localhost:3000/mcp -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

GET /openapi.json is an OpenAPI 3.1 document built from the same route catalog: every paid path with its parameters, examples and 402 response, plus an x-x402 block per operation with the network, asset, receiving wallet, facilitator and the minimum and maximum price. Import it into any API directory or client generator.

tools/list returns only the fields the MCP spec defines, because strict clients reject unknown keys. Call a tool with no payment and you get the x402 challenge back instead of an error. Pay it, then repeat the call with the base64 payment payload under params._meta["x402/payment"].

The MCP handler settles nothing itself. It forwards the call to the same HTTP route over loopback with the caller's payment header attached, so verification, settlement, attribution and replay protection all run through one path. There is no way to reach creator data through MCP without paying.

The agent is registered on Celo mainnet as id 9852. Its on-chain metadata lists nine services: the web app, the A2A card, the MCP endpoint, the OpenAPI document, and one entry per paid route. The logo is written as the spec image field. Route entries keep their path parameters as {id} templates, since a concrete creator id would become a dead link the moment that creator left the catalog.

Reputation works the way ERC-8004 intends. The registry refuses feedback from the agent's owner and from any operator, so the wallet that paid is the only wallet that can rate:

npm run x402:give-feedback -- --tx 0x<settlement hash> --endpoint https://api.streamlivr.com/api/v1/agent/ping

The script builds a document holding the settlement hash, buyer, amount, asset and payee, hashes it with keccak256, and passes it as a base64 data: URI next to that hash, so anyone can re-hash the document and compare. Gas is paid in USDC through fee abstraction, which means a buyer with no CELO can still leave a rating. npm run x402:agent-status reads the whole record back from the chain.

One contract detail is easy to misread. getSummary on this deployment returns an average, not a sum. Two entries of 100 read back as count 2, average 100.

Where the API, MCP, and Agent Skills are listed globally

Streamlivr's creator discovery APIs, Model Context Protocol (MCP) servers, and autonomous agent skills are publicly listed and discoverable across major AI registries, developer environments, and LLM marketplaces:

Ecosystem / LayerSurface NameVerified StatusReference & Listing Link
Official Global MCP Registryregistry.modelcontextprotocol.io
(Anthropic & Linux Foundation)
🟒 LIVE & ACTIVEPublished as io.github.StreamlivrApp/streamlivr@1.0.0. Resolves to https://api.streamlivr.com/mcp.
OpenAI & ChatGPT EcosystemChatGPT Plugins / Custom MCP🟒 ACTIVEInstalled and verified as remote MCP plugin. Turnkey Action schema available at https://api.streamlivr.com/openapi.json.
Developer IDE ExtensionsCursor Directory🟒 LIVELive community plugin at cursor.directory/plugins/streamlivr.
Universal MCP CatalogsGlama.ai🟒 LIVELive connector at glama.ai/mcp/connectors/com.streamlivr.api/streamlivr. Verified Ownership, Healthy status, TDQS: 3.9/5.0.
Autonomous Celo Marketplacesusebuy.ai (Celo Buy Skill)🟒 LIVEApproved listing in celo-org/buy-skill for autonomous machine purchasing via @celo/buy.
On-Chain Agent IdentityAigora.org (ERC-8004)🟒 LIVEOn-chain agent record on Celo Mainnet: aigora.org/services/42220_0x8004a..._9852.
Open-Source LLMs & Multi-ModalAgensi.io (Meta Muse & Llama)🟒 LIVEOfficial skill listing at agensi.io/skills/streamlivr.
Decentralized x402 Crawlersagent402.tools & x402.newπŸ€– INDEXEDAutomatically indexed via extensions.bazaar payload and /.well-known/x402.
Frontier LLM Agent MarketplacexAI Grok Plugin Marketplace🟑 IN REVIEWSubmitted via PR #1684 (feat(plugins): add Streamlivr MCP creator & music discovery plugin).
Developer Coding AssistantCline Marketplace🟑 SUBMITTEDSubmission issue filed on github.com/cline/mcp-marketplace.

For setup snippets across Claude Desktop, Cursor, OpenAI/ChatGPT, and Python/TypeScript agents, see the complete Global Distribution Guide.

On-chain proof

Every hash from our own runs is listed with its explorer link in docs/ONCHAIN-PROOF.md: nine mainnet settlements, creator payouts, the ERC-8004 registration and metadata update, two reputation entries, and the Sepolia history behind them. Volume is small and the buyer is our own canary wallet. The page says so rather than dressing it up.

Verify it

npm run typecheck
npm test
npm run demo
npm run x402:buyer-smoke                                  # needs a funded buyer key
npm run x402:verify-attribution -- 0x<txHash>
npm run x402:agent-status

The buyer canary wraps fetch with the x402 client, pins the asset and a per-payment cap, and fails unless the response carries a payment-response header with a real transaction hash. A 200 on its own does not pass. The one exception is a page with no rows: the seller answers x-no-charge, the canary reports a pass with no hash, and nothing was submitted.

For a testnet pass: request a paid route and read the 402, fund a throwaway buyer with Sepolia USDC from https://faucet.circle.com, run the canary, then check the hash on https://celo-sepolia.blockscout.com and confirm GET /demo/settlements recorded the same payment with a 60/40 split. The buyer needs no testnet CELO, because gas is sponsored.

Assets

AssetMainnetSepoliaDecimalsEIP-712
USDC0xcebA9300f2b948710d2653dD7B07f33A8B32118C0x01C5C0122039549AD1493B8220cABEdD739BC44E6USDC / 2
USDT0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5enot settled6Tether USD / 1
USAT0xD2ab3C9A02DBBAB236BfEC45D1d755DF4267F771not settled6Tether America USD / 1

USDm and the other Mento stablecoins are not supported by this facilitator. They implement EIP-2612, and the hosted settlement engine expects EIP-3009, so a payment would return 402 and never settle. The config rejects them at startup.

src/x402/config.ts refuses to boot when the network, asset, address or decimals disagree, which keeps a mainnet wallet from being paired with a Sepolia token.

Celo pieces in use

PieceWhere it shows up
x402 v2 exact scheme with EIP-3009Every paid route, src/x402/config.ts
Hosted Celo facilitator, gas sponsoredsrc/x402/facilitator.ts, settlement in demo/server.ts
Fee abstractionPayouts, agent registration and reputation writes pay gas in the settlement asset
ERC-8021 attributionAppended to every transaction this code signs, checked by npm run x402:verify-attribution
ERC-8004 identityAgent id 9852 on Celo mainnet, metadata built from the route catalog
ERC-8004 reputationBuyer-signed feedback, read back by npm run x402:agent-status

Settlements are broadcast by the facilitator's relayer, so the seller cannot attach an attribution suffix to them. Those are credited to the agent wallet registered with the programme instead. Transactions Streamlivr signs itself, such as payouts and the agent registration, carry the suffix in calldata. There is no way to add one afterwards.

Scope

This repo holds the payment modules, their tests, five operator scripts, a runnable reference server and the demo site. The production API, the mobile app and the admin console live in Streamlivr's private repositories. Three reference files reproduce the wiring that lives there: reference/admin-payout-routes.ts for prepare, approve, execute and reconcile, reference/admin-revenue-routes.ts for the reporting queries, and reference/local-kyc-override.ts for the development-only eligibility switch.

What is not here yet: a scheduled job that compares stored settlement hashes against the receiving wallet's chain history, payout signing behind a KMS or hardware signer, and a distributed lock so more than one API instance can send payouts safely. Those are the next three things we would build before real volume. Today the treasury sends are serialized in process, and the code says so in a comment rather than pretending otherwise.

Security

The facilitator key is attached to server-to-server calls and never reaches a client. No private key is read from a request body, a query string or a bundle. The buyer canary and the payout signer are server-side only.

Amounts stay as integer strings in token base units from the moment a payment is read to the moment a payout is signed. Balances larger than Number.MAX_SAFE_INTEGER do not lose a unit, and a failed transfer releases the reserved amount by construction rather than by a correction job.

npm audit reports five advisories, all in the test toolchain (vitest, vite, esbuild). npm audit --omit=dev is clean, so nothing that runs in production is affected. Clearing them means a major vitest upgrade, which is queued rather than rushed in the week of a submission.

References

MIT licensed. See LICENSE.