Marketplace

The one market between orgs: agents and personas to hire, apps, skills, MCP servers and tools to buy, each sold by the org that owns it, and the two-sided work one org buys from another.

Also for this capability: API · CLI · MCP · SDKs

The one market between orgs: agents and personas to hire, apps, skills, MCP servers and tools to buy, each sold by the org that owns it, and the two-sided work one org buys from another.

Base URLhttps://api.hanzo.ai
Operations23
AuthAuthorization: Bearer $HANZO_API_KEY

Specification

HIP-1137 · Marketplace — Listings and Installs — Draft · read the specification →

/v1/marketplace is the shop for tools and agents: browse, install into your project, publish your own free or priced. It is a thin layer over the unified tool plane — discovery reads the tool registry, install and uninstall ARE the registry's activation writes — plus a listing store that says what a tool costs and which wallet is paid. Marketplace never dispatches a tool and never moves money itself. It is implemented in hanzoai/cloud at apps/marketplace (HIP-0106).

Motivation

A marketplace that keeps its own installed-tools table drifts from the registry that actually dispatches, and a price that lives only in one process's memory is not enforced anywhere else. Both defects were real here: the price table, the charger and the wallet resolver were process-globals, the fleet runs one process per app, so in the settling process the table was nil — and reading a nil table as "nothing is priced" made every listed tool free the moment the fleet split (apps/marketplace/marketplace.go:16-30).

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.

§1 One store, and what is deliberately not in it

The listing store is one encrypted SQLite file, the deployment's own marketplace (apps/marketplace/store.go:51), keyed by (publisher_org, id) — a publisher only ever mutates its own listings. Installs are NOT in it: install and uninstall are the tool registry's activation writes for the caller's (org, project), so there is one store and one truth for what is active (apps/marketplace/marketplace.go:8-15).

§2 The address

Six operations under /v1/marketplace, all typed: discovery (catalog plus listing overlay plus installed flag), the caller org's own listings, publish, unpublish, install, uninstall. A published listing must name a tool that already resolves in the publisher's own scope, so a listing can never advertise a capability that does not exist; a priced listing must name the payout wallet, so a monetized offer is never unpayable; the price is exact to 18 decimal places, so a per-call price below a cent is a real price and not a rounded-away zero.

§3 Money: the table here, the rail elsewhere

Marketplace itself is free (cloud.Free, plugin/marketplace/main.go). A monetized listing is enforced per call by the x402 rail, and the enforcement is four internal-plane operations, each served by the process that owns the answer: tools asks x402 to settle; x402 asks marketplace what it costs and who is paid; x402 asks wallets to resolve the payee; x402 asks commerce to credit it (apps/marketplace/marketplace.go:32-39). The in-process seam stays the fast path where an owner is co-resident; both paths are the same policy and both fail closed, asserted against each other by a one-process test and a five-process test (apps/marketplace/marketplace.go:41-44).

On the plane, the price answer carries the row's recipient, never a request's: a buyer that could state either would buy at its own price or redirect the credit. priced=false means FREE and is an ANSWER — an unlisted tool lands there — while a store failure is an ERROR, because "I could not look it up" must never read as "it costs nothing" (apps/marketplace/rpc.go:22-33,47-50). The price op reads no tenant and requires none: a price is the shop window, the same figure for everyone.

§4 Tenancy

Every REST operation is org-gated on the validated principal (HIP-0026), parked by the bridge the composer installs once at the root. The payee is structurally the publisher's: PublisherOrg is the payee org, and the wallet id resolves only within the org that is asked for, which makes a cross-org credit unconstructible rather than merely unlikely (apps/marketplace/store.go:25-31).

§5 Events, observability, stage, upstream

It publishes nothing on the bus and emits nothing beyond the request span every route gets. The stage is beta (manifest/apps.go:430, Stage: Beta): reached by flag until promoted (HIP-0139 §8). It derives from no upstream.

Rationale

The alternative to "install is activation" is a marketplace-owned installs table, which is a second copy of the registry's fact — the defect the plugin contract names — and it would disagree first exactly where it matters, on whether a priced tool is active. The alternative to asking the owning process for the price was replicating the table into the rail's process, a cache that turns every price change into a coherence problem on the money path.

Security Considerations

The money path is the exposure, and its two failure shapes are opposite. Fail open: a missing price table read as "free" dispensed paid tools for nothing — closed by making absence an error and free an explicit answer. Redirection: a caller-influenced recipient or price turns the rail into a payout to the attacker — closed by binding both to the listing row, publisher-owned and publisher-org-payable only. The listing store's own writes are bounded by the (publisher_org, id) key, so a tenant can unpublish only what it published.

Four surfaces

SurfaceReaches this capability asCoverage
RESTmarketplace at its own prefix23 operations
CLIhanzo marketplace …6 of 23 — the CLI pins the document on its own clock
SDKMarketplaceApi in every published client23 methods
MCPtool marketplace on https://api.hanzo.ai/v1/mcp23 operations, 3 under the document's own id — ask describe for the rest

Quickstart

export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys

Then the first call — a read that needs nothing but the key. GET /v1/marketplace, operation get_marketplace:

hanzo marketplace get
import { Configuration, MarketplaceApi } from 'hanzoai';

const api = new MarketplaceApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getMarketplace();
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import MarketplaceApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = MarketplaceApi(client).get_marketplace()
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.MarketplaceAPI.GetMarketplace(context.Background()).Execute()
if err != nil {
	return err
}
use hanzo_client::apis::{configuration::Configuration, marketplace_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = marketplace_api::get_marketplace(&cfg, Default::default()).await?;
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.MarketplaceApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new MarketplaceApi(client).getMarketplace();
curl https://api.hanzo.ai/v1/marketplace \
  -H "Authorization: Bearer $HANZO_API_KEY"

MCP reaches marketplace through the marketplace tool, which names its 23 operations with its own verbs — this one among them, under a name only MCP declares. describe explains any of them:

curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "list_marketplaces"
         }
       }
     }'

Answers 200 with object — ok.

Endpoints

EndpointWhat it does
POST /v1/marketplace/installActivates one tool for the caller's own org and project.
POST /v1/marketplace/jobs/{id}/acceptAccepts a job the caller's org was hired for: the seller takes the work on and the clock toward its deadline is the seller's.
POST /v1/marketplace/jobs/{id}/cancelTakes back a job the caller's org opened, before the seller accepts it.
POST /v1/marketplace/jobs/{id}/declineDeclines a job the caller's org was hired for, before any work.
POST /v1/marketplace/jobs/{id}/deliverRecords delivery of a job the caller's org accepted, before its deadline, and starts the review window: the buyer releases or disputes within it, or it releases itself when it closes.
POST /v1/marketplace/jobs/{id}/disputeStops a job for a ruling, as either party: before delivery (and before the deadline), or within the review window after it.
POST /v1/marketplace/jobs/{id}/feedbackRates the other party of a settled job — the seller when the caller's org bought, the buyer when it sold — once per party per job, and never edited.
POST /v1/marketplace/jobs/{id}/refundRefunds a job the caller's org accepted, as the seller, at any point before it is paid: nothing was moved, so the buyer's authorization is given up, never settled, and the amount it set aside returns to the buyer's wallet.
POST /v1/marketplace/jobs/{id}/releaseReleases a job the caller's org is paying for, paying the seller the whole amount: the buyer's authorization is settled on the rail, once, and the rail states the payment.
GET /v1/marketplace/jobs/{id}Reads one job the caller's org is a party to.
GET /v1/marketplace/jobsLists the jobs the caller's org is a party to, newest first: as buyer, the work it hired — with the quotes it has not paid and the jobs being funded, each with the attempt that made it, so a hire whose answer was lost is found here — and as seller, the work it was hired for, which never includes another org's quote.
POST /v1/marketplace/jobsHires another org for a piece of work, through a public listing or by a direct offer, and answers 201 with the job — open, waiting for the seller.
PATCH /v1/marketplace/listings/{id}Edits one of the caller org's listings — its copy, price, payout wallet, visibility and documentation — under the same rules publish applies, and answers the listing as it now stands.
DELETE /v1/marketplace/listings/{id}Withdraws one of the caller org's listings from the marketplace and answers 204.
GET /v1/marketplace/listingsReturns the listings the caller's own org has published — what this org is offering, not what it can buy.
POST /v1/marketplace/listingsOffers one thing the caller's org owns on the marketplace — an agent, persona, app, skill or MCP server, or, for the platform, a tool sold per call — optionally monetized.
POST /v1/marketplace/seller/payout/verifyBinds the caller org's payout wallet: the signature over its outstanding challenge is recovered, and must recover to the wallet's address, before the challenge expires.
POST /v1/marketplace/seller/payoutStarts binding one of the caller org's wallets as the wallet it is paid into: answers a challenge naming the org, the wallet and its address, to be signed with that wallet within fifteen minutes and sent to POST /v1/marketplace/seller/payout/verify.
GET /v1/marketplace/sellerAnswers where the caller's org stands as a seller, in one read: its founders' identity verification (KYC) and legal entity (KYB), its tax form and whether it is certified and valid, its own sanctions screening — only what an org may see about itself — the payout wallet it proved, the TaxPrincipalCredentials signed for its agents, what it earned in the year from the economic events the rails stated, and the 1099s payers furnished it.
GET /v1/marketplace/shop/{id}Reads one public listing as the shop shows it — its seller's public face, its reputation and the ways to buy it.
GET /v1/marketplace/shopSearches every public listing of every kind — agents and personas to hire, apps, skills, MCP servers and tools — newest first, with facets by kind, category, price and rating, paged.
POST /v1/marketplace/uninstallDeactivates one tool for the caller's own org and project, so it stops being dispatchable there.
GET /v1/marketplaceLists every tool and agent the caller can reach in their own org and project, enriched with any public listing's title, category and price, and with installed=true on the ones already activated for that scope.

All Hanzo APIs · Interactive reference

Was this page useful?