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
| Route | Price | Returns | Attribution |
|---|---|---|---|
GET /api/v1/agent/ping | 10000 | Liveness check that proves settlement works | none |
GET /api/v1/agent/listings | 10000 each | Public creator listings, searchable and paged | every creator on the page |
GET /api/v1/agent/posts | 10000 each | Public posts: captions, tags, media, engagement | the owner of the audio on each post |
GET /api/v1/agent/catalog | 10000 each | Catalog metadata with the owner of each recording | the artist who owns each recording |
GET /api/v1/agent/creator/:id | 10000 | One public profile with their public counts | that creator |
GET /api/v1/agent/pricing | free | The price rule, the asset and worked examples | none |
POST /api/v1/agent/chat/interpret | free | Turns a visitor sentence into one call | none |
GET /api/v1/agent/stats | free | How many creators, posts and tracks there are, and the busiest tags | none |
GET /.well-known/agent.json | free | A2A agent card with prices and the ERC-8004 identity | none |
GET /.well-known/mcp.json | free | MCP server card listing the same routes as tools | none |
GET /openapi.json | free | OpenAPI 3.1 document for every route, with x402 pricing per operation | none |
POST /mcp | paid per tool | MCP JSON-RPC, same prices as the HTTP routes | same as the route called |
GET /api/v1/agent/reputation | free | On-chain ERC-8004 reputation plus settled volume | none |
GET /healthz | free | Process health | none |
GET /demo/settlements | free | What this process settled, with the split | none |
GET /api/v1/agent/demo/settlements | free | The same ledger in the shape the demo site renders | none |
GET /api/v1/agent/demo/creators | free | Per-creator balances for the demo site | none |
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 row | Who is credited |
|---|---|
| A post with the publisher's own recording | the publisher |
| A post carrying another creator's sound | the creator who owns the sound |
| A post whose audio is a commercial recording | the artist, when that artist has a Streamlivr account |
| Audio nobody on Streamlivr owns | nobody; a page with no credited creator is free |
| A catalog recording owned by a creator | the owner |
| A catalog recording with no owner here | nobody; 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.totalis 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.nextCursoris 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.tsis the whole implementation, and it is unit tested.qis 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.limitis 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
- An agent asks for a paid route with no payment and gets
402plus the invoice in thepayment-requiredheader. The amount is one cent per creator that request would credit. - It signs an EIP-3009
TransferWithAuthorizationoff-chain. No approval transaction, no gas. - It repeats the request with the
payment-signatureheader. - The seller asks the Celo facilitator to verify the signature, then to settle it.
- USDC moves from the buyer to
X402_PAY_TOinside the token contract, in one transfer. The facilitator never holds the funds. - 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-responseheader 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:
| Gate | Source | Blocks when |
|---|---|---|
| Identity | User.kycStatus | anything other than VERIFIED |
| Fraud suspension | User.isFraudSuspended | true |
| Fraud score | User.fraudRiskScore | 80 or higher |
| Payout wallet | primary Celo wallet | missing |
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 / Layer | Surface Name | Verified Status | Reference & Listing Link |
|---|---|---|---|
| Official Global MCP Registry | registry.modelcontextprotocol.io (Anthropic & Linux Foundation) | π’ LIVE & ACTIVE | Published as io.github.StreamlivrApp/streamlivr@1.0.0. Resolves to https://api.streamlivr.com/mcp. |
| OpenAI & ChatGPT Ecosystem | ChatGPT Plugins / Custom MCP | π’ ACTIVE | Installed and verified as remote MCP plugin. Turnkey Action schema available at https://api.streamlivr.com/openapi.json. |
| Developer IDE Extensions | Cursor Directory | π’ LIVE | Live community plugin at cursor.directory/plugins/streamlivr. |
| Universal MCP Catalogs | Glama.ai | π’ LIVE | Live connector at glama.ai/mcp/connectors/com.streamlivr.api/streamlivr. Verified Ownership, Healthy status, TDQS: 3.9/5.0. |
| Autonomous Celo Marketplaces | usebuy.ai (Celo Buy Skill) | π’ LIVE | Approved listing in celo-org/buy-skill for autonomous machine purchasing via @celo/buy. |
| On-Chain Agent Identity | Aigora.org (ERC-8004) | π’ LIVE | On-chain agent record on Celo Mainnet: aigora.org/services/42220_0x8004a..._9852. |
| Open-Source LLMs & Multi-Modal | Agensi.io (Meta Muse & Llama) | π’ LIVE | Official skill listing at agensi.io/skills/streamlivr. |
| Decentralized x402 Crawlers | agent402.tools & x402.new | π€ INDEXED | Automatically indexed via extensions.bazaar payload and /.well-known/x402. |
| Frontier LLM Agent Marketplace | xAI Grok Plugin Marketplace | π‘ IN REVIEW | Submitted via PR #1684 (feat(plugins): add Streamlivr MCP creator & music discovery plugin). |
| Developer Coding Assistant | Cline Marketplace | π‘ SUBMITTED | Submission 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
| Asset | Mainnet | Sepolia | Decimals | EIP-712 |
|---|---|---|---|---|
| USDC | 0xcebA9300f2b948710d2653dD7B07f33A8B32118C | 0x01C5C0122039549AD1493B8220cABEdD739BC44E | 6 | USDC / 2 |
| USDT | 0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e | not settled | 6 | Tether USD / 1 |
| USAT | 0xD2ab3C9A02DBBAB236BfEC45D1d755DF4267F771 | not settled | 6 | Tether 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
| Piece | Where it shows up |
|---|---|
x402 v2 exact scheme with EIP-3009 | Every paid route, src/x402/config.ts |
| Hosted Celo facilitator, gas sponsored | src/x402/facilitator.ts, settlement in demo/server.ts |
| Fee abstraction | Payouts, agent registration and reputation writes pay gas in the settlement asset |
| ERC-8021 attribution | Appended to every transaction this code signs, checked by npm run x402:verify-attribution |
| ERC-8004 identity | Agent id 9852 on Celo mainnet, metadata built from the route catalog |
| ERC-8004 reputation | Buyer-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
- x402 on Celo, agent-readable guide: https://x402.celo.org/SKILL.md
- Live facilitator config: https://x402.celo.org/api/config
- Celo docs: https://docs.celo.org
- ERC-8004 agent registries: https://eips.ethereum.org/EIPS/eip-8004 and https://8004scan.io/agents/celo/9852
- ERC-8021 attribution tags: https://github.com/celo-org/attribution-tags
- EIP-3009: https://eips.ethereum.org/EIPS/eip-3009
MIT licensed. See LICENSE.