# Seller quickstart

> Sell an MCP tool on the hosted platform. Sign up, ask for verification, publish and serve with one call, make a test purchase, and see the sale land.

This walkthrough sells one MCP tool for $0.10 a call. For an HTTP endpoint, the steps are the same except step 3. See [Sell an HTTP endpoint](https://docs.zanora.dev/sellers/rest.md). Steps marked 👤 need a person.

### Step 1: Create a provider account 👤

At [console.zanora.dev](https://console.zanora.dev/#signup), choose **Seller**, enter your email, organisation and password, and click the emailed link. The console shows your **provider API key once**. Save it:

```bash
export ZANORA_API_KEY='zk.akey_….…'
curl -s https://api.zanora.dev/v1/whoami -H "x-api-key: $ZANORA_API_KEY"   # → "kind":"provider","providerId":"prv_…"
```

### Step 2: Ask to be verified

In the console, **Seller → Publish** shows a banner with a **Request verification** button. Or:

```bash
curl -s -X POST https://api.zanora.dev/v1/providers/prv_…/verification-request \
  -H "x-api-key: $ZANORA_API_KEY" -H 'content-type: application/json' \
  -d '{"note":"Market data API, live since 2024, ~40k calls/month","contact":"ops@acme.example"}'
```

You can carry on while you wait. Your listing exists, but buyers won't find it until an operator approves you.

### Step 3: Publish and serve in one call

```bash
npm install @zanora/middleware-mcp @modelcontextprotocol/sdk zod express
```

```ts title="server.ts"
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { zanoraSeller } from "@zanora/middleware-mcp";
import { z } from "zod";

// Reads ZANORA_API_KEY (a provider key). The gateway defaults to https://api.zanora.dev.
const seller = zanoraSeller();
const paid = await seller.sell({
  tool: "quote",
  description: "Latest price for a stock ticker",
  category: "finance",
  price: "$0.10",
  url: "https://acme.example/mcp",   // where buyers reach this server; or set ZANORA_PUBLIC_URL
});

const app = express().use(express.json());
app.post("/mcp", async (req, res) => {
  const server = new McpServer({ name: "acme-quotes", version: "1.0.0" });
  server.tool("quote", "Latest price for a ticker. Costs $0.10.", { ticker: z.string() },
    paid(async ({ ticker }) => {
      const answer = { ticker: String(ticker), price: await lookup(String(ticker)) };
      // structuredContent is what the receipt's hash covers, so it's what the buyer can prove they received.
      return { content: [{ type: "text", text: JSON.stringify(answer) }], structuredContent: answer };
    }),
  );
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  res.on("close", () => void transport.close());
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});
app.listen(4025);

async function lookup(ticker: string) { return 187.42; }
```

The first start **publishes** the capability. Later starts **reconcile** it: if you've changed the price or description in code, the listing is updated to match. A call without payment gets back a signed price quote, and your handler runs only once the call is paid for.

> **Warning — The URL must be public:**
>
> Buyers' agents call `https://acme.example/mcp` themselves. While you develop, put the port behind a tunnel (for example `ngrok http 4025`) and publish that URL instead.

### Step 4: Make a test purchase

Once you're verified, buy from yourself with any buyer setup, for example the [buyer quickstart](https://docs.zanora.dev/quickstart.md):

```text
Use zanora_discover to find "stock price" tools, then buy a quote for AAPL.
```

Before verification, you can still check the handshake. A call without payment should come back as a `PAYMENT_REQUIRED` result carrying the challenge.

### Step 5: Watch the sale land

**Seller → Sales** lists every paid call with its receipt. **Seller → Earnings** shows your balance. The 3% fee rounds down to the cent, so at $0.10 it rounds to zero and you keep all `"10"`. At $1.00 you would keep `"97"`. Or from the API:

```bash
curl -s "https://api.zanora.dev/v1/receipts?limit=10" -H "x-api-key: $ZANORA_API_KEY"
```

### Step 6: Set a payout destination and cash out 👤

In **Seller → Earnings**, save a bank account id (`ext_…`) or a USDC address, then request a payout. See [Earnings and payouts](https://docs.zanora.dev/sellers/payouts.md).

> **Tip — Runnable versions:**
>
> The platform repository has complete starters: `starters/c-seller-mcp` (this page) and `starters/b-seller-rest` (HTTP), each with a `--request-verification` flag that does step 2.
