Install guide
01 · Node
@zeam-labs/pass
- Requires
- Node 18 or later
- Version
- 1.0.2
Install
npm install @zeam-labs/pass express
app.mjs
import express from 'express'
import { pass } from '@zeam-labs/pass'
const agents = pass({
name: 'acme',
site: 'https://acme.example',
mode: 'paywall',
price: '0.02',
payout: '0xYourWalletAddress',
})
agents.tool({
name: 'add',
description: 'Adds two numbers.',
inputSchema: {
type: 'object',
properties: { a: { type: 'number' }, b: { type: 'number' } },
required: ['a', 'b'],
},
run: async ({ a, b }) => ({ sum: a + b }),
})
const app = express()
app.use(agents.express('/agents'))
app.listen(3000)
Put your wallet address in payout. pass() refuses a placeholder.
node app.mjs
Your tools are at http://localhost:3000/agents/mcp. On a fetch host, serve agents.handle('/agents') instead of Express. With no name, pass() reads ./pass.json.
02 · Python
zeam-pass
- Requires
- Python 3.9 or later
- Installs
- coincurve, pycryptodome
Install
pip install zeam-pass fastapi uvicorn
app.py
from fastapi import FastAPI
from zeam_pass import Pass
agents = Pass(name="acme", payout="0xYourWalletAddress", price="0.01")
@agents.tool(description="Multiplies two numbers.", input_schema={"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]})
def multiply(a, b):
return {"product": a * b}
app = FastAPI()
app.mount("/agents", agents.asgi())
Put your wallet address in payout.
uvicorn app:app --port 3000
Your tools are at http://localhost:3000/agents/mcp. Flask mounts the WSGI app:
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {"/agents": agents.wsgi()})
03 · WordPress
ZEAM Pass
- Requires
- WordPress 6.0 or later, tested to 7.1
- PHP
- 7.4 or later, with GMP or BCMath, and sodium
- Version
- 1.0.1
Your site must be reachable from the internet and use pretty permalinks.
Install
- Plugins → Add New, search “ZEAM Pass”, Install Now.
- Or download
zeam-pass.zipfromwordpress.org/plugins/zeam-passand use Plugins → Add New → Upload Plugin.
Set up
- Download the plugin: https://zeampass.com/downloads/zeam-pass-1.0.1.zip (its SHA-256 is next to it, at the same address plus .sha256). Plugins → Add New → Upload Plugin, choose the zip, Install Now, then Activate. Coming to the WordPress.org plugin directory.
- Settings → ZEAM Pass.
- Connect your payout wallet: your browser wallet, or paste the address.
- Choose gate, paywall or both. Paywall: set a price per call; optional, a price or Free per tool and free calls an hour per address. Gate: list the addresses of the keys you admit. "Contact for access": a web address or a mailto: address where an agent asks to be admitted. Agents see it in every 402 and when a key is refused. Empty: your site's address; your admin email is never shown.
- Gate, optional: the Grants box signs a grant with your browser wallet, when it holds a listed key.
- Save. The page shows your split, earnings, gate usage and the credit wallet to fund.
Add an encryption key of at least 32 random characters to wp-config.php and keep it:
define('ZEAM_PASS_KEY', '…');
Agents get two tools, search_posts and read_post, over your posts.
04 · Settings
Settings.
mode
Paywall, gate or both.
- Node
mode- Python
mode- WordPress
- Mode
- Default
- paywall
price
USD per call, up to 6 decimals ("0.02"); the price of a tool with none of its own; required for paywall and both unless prices is set.
- Node
price- Python
price- WordPress
- Price per call (USD)
- Default
- none
payout
The wallet address earnings go to; a gate's zero-value proofs name it, and nothing is paid to it.
- Node
payout- Python
payout- WordPress
- Payout wallet
- Default
- none
admit
The addresses of the keys a gate admits (gate and both).
- Node
admit- Python
admit- WordPress
- Admitted keys
- Default
- []
contact
An https:// URL or a mailto: address; goes in every 402, in the 403 refused answer (how and contact) and in openapi.json info.contact.
- Node
contact- Python
contact- WordPress
- Contact for access
- Default
- Node and Python none; WordPress your site's address
name
2 to 32 of a-z, 0-9 and -, starting with a letter; fixes your split's address.
- Node
name- Python
name- WordPress
- Name
- Default
- none
site
Your public origin, used in the 402's resource and refund URL.
- Node
site- Python
site- WordPress
- your site
- Default
- the request's origin
onEmpty
Gate credit at 0: refuse, or allow and warn.
- Node
onEmpty- Python
on_empty- WordPress
- When gate credit runs out
- Default
- refuse
state dir
Keys, channel records, gate usage; relative to the working directory.
- Node
stateDir- Python
state_dir- WordPress
- its database
- Default
- Node $PASS_STATE_DIR or ./.pass; Python ~/.zeam-pass/<name>, or PASS_STATE_DIR
05 · Pricing
Per call, per tool, per unit, by time.
Per call
One price for every tool with none of its own.
- Node
price- Python
price- WordPress
- Price per call (USD)
- Default
- none; paywall and both need price or prices
Per tool
A tool's own price, else prices[tool], else price.
- Node
priceontool(), orprices: { tool: '0.05' }or(tool, args) => price- Python
@agents.tool(price=...), orprices- WordPress
- Per tool: a price per tool; the
zeam_pass_pricesfilter - Default
- none
Per unit used
The call reserves the tool's price. The tool reports whole units. Charge: units × unit, at most the reserve. No units reported: nothing charged.
- Node
unitontool();meter.units(n)inrun- Python
@agents.tool(unit=...);units(n)- WordPress
unitin thezeam_pass_toolsfilter;$meter->units($n)- Default
- none
By time
The agent buys blocks with buy_time, opens a line, and calls meter: 'time' tools on it with no payment per call. Time burns while a call runs, plus idleMs after each. Unburned time goes back with a refund. Paywall and both only. Adds buy_time, line and POST <base>/line.
- Node
time: { block, blockMs, idleMs, maxBlocks };meter: 'time'ontool()- Python
time={...};@agents.tool(meter="time")- WordPress
- Line time (USD per block) and ms;
'meter' => 'time'in thezeam_pass_toolsfilter - Default
- off; blockMs 250, idleMs 0, maxBlocks 14,400 and at most $1,000 of blocks
Free
No payment, no key. Arguments still checked.
- Node
free: ['ping'], orfree: trueontool()- Python
free=[...], or@agents.tool(free=True)- WordPress
- Free, per tool
- Default
- none
Free limit
Free calls per tool, per client address, per clock hour. Over it: 429 free_limit.
- Node
freeLimit: 60- Python
free_limit=60- WordPress
- Free calls an hour per address
- Default
- no limit
app.mjs
import express from 'express'
import { pass } from '@zeam-labs/pass'
const run = async () => ({ ok: true })
const agents = pass({
name: 'acme', payout: '0xYourWalletAddress', price: '0.02', freeLimit: 60,
time: { block: '0.00025', blockMs: 250, idleMs: 0, maxBlocks: 14400 },
})
agents.tool({ name: 'report', price: '0.05', run })
agents.tool({ name: 'ping', free: true, run })
agents.tool({
name: 'words', price: '0.05', unit: '0.0001',
run: async ({ text }, meter) => {
meter.units(text.length)
return { ok: true }
},
})
agents.tool({ name: 'scan', meter: 'time', run: async (args, { signal, deadline }) => ({ until: deadline }) })
const app = express()
app.use(agents.express('/agents'))
app.listen(3000)
app.py
from fastapi import FastAPI
from zeam_pass import Pass, deadline_ms, units
agents = Pass(name="acme", payout="0xYourWalletAddress", price="0.02", free_limit=60,
time={"block": "0.00025", "blockMs": 250, "idleMs": 0, "maxBlocks": 14400})
@agents.tool(price="0.05")
def report():
return {"ok": True}
@agents.tool(free=True)
def ping():
return {"ok": True}
@agents.tool(price="0.05", unit="0.0001", input_schema={"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]})
def words(text):
units(len(text))
return {"ok": True}
@agents.tool(meter="time")
def scan():
return {"until": deadline_ms()}
app = FastAPI()
app.mount("/agents", agents.asgi())
report: $0.05 per call. ping: free, 60 calls an hour per address. words: reserves $0.05, charges $0.0001 per unit. scan: burns line time; without a line, a paid call of $0.02 that runs up to 20 s.
The 402 carries this call’s amount, pricing for it, and prices for every tool. tools/list: _meta["zeam-pass/price"]. OpenAPI: x-price.
06 · What it creates
Keys and split.
Settle key
Made on first run. It signs your claims and refunds and holds no money. Node and Python keep it in keys.json in the state dir, mode 0600; set PASS_KEY_SECRET to seal it. WordPress keeps it encrypted in its database. Back it up: a lost settle key cannot claim what your channels owe you.
Credit wallet
Made for gate and both. In gate mode, send it USDC on Base; it needs no ETH. In both mode it needs nothing.
Split
Your earnings go to a split contract that nobody owns. It pays 90.01% to your payout wallet and is created by your first payout. Its address depends on your name and payout wallet.
- Node, Python
agents.status():receiver(split),settleKey,creditWallet- WordPress
- Settings → ZEAM Pass: Your split, Settle key, Credit wallet
07 · What agents see
MCP, HTTP, OpenAPI, 402.
- MCP
/agents/mcp·/wp-json/zeam-pass/mcp- HTTP
POST /agents/v1/<tool>·POST /wp-json/zeam-pass/v1/<tool>- OpenAPI
/agents/openapi.json·/wp-json/zeam-pass/openapi.json- Refunds
POST /agents/refund·POST /wp-json/zeam-pass/refund
An unpaid call answers 402 with your price: accepts[].amount in micro-USDC, "20000" is $0.02. Over MCP, the tool result carries the same terms. The agent pays in USDC on Base over x402 and gets the answer.
Bad arguments, or a tool that fails, cost the agent nothing. What an agent deposits and does not spend, it takes back at the refunds route.
Paying needs no ETH. A refund the seller sends needs no ETH. selfSend, initiateWithdraw and finalizeWithdraw are transactions from your wallet and need ETH on Base for gas.
A gate
A gate’s 402 asks for amount: "0". The agent signs it with its own key, as a zero-value x402 payment: nothing is paid, the key needs no funds and no ETH, and nothing goes on chain. You list the keys you admit in admit.
A key you admit lets one other key in with a grant, sent as the x-grant header. Sign one with signGrant in Node, sign_grant in Python, or the Grants box on the WordPress settings page. The grant works only for the key it names.
08 · Pass link
Let buyers fund a pass for your service.
Link to https://zeampass.com/pass with your MCP URL in seller. The page reads your prices and funds a pass that pays only you.
https://zeampass.com/pass?seller=<your MCP URL, URL-encoded>
A button for your site:
<a href="https://zeampass.com/pass?seller=https%3A%2F%2Fyour.site%2Fagents%2Fmcp">Give your agent a pass for our service</a>
seller takes your MCP URL or its base: https://your.site/agents/mcp, https://your.site/agents, or https://your.site/wp-json/zeam-pass/mcp on WordPress.
09 · Costs
You keep 90.01% of every sale.
- Paywall
- you keep 90.01% of each sale; ZEAM pays the gas
- Gate
- 10,000 checks a month free, then 2,000 checks for $1 USDC
- Both
- you keep 90.01% of each sale; paid calls are not gate checks
- Payouts
- to your wallet once a payout covers its gas; the first waits for about $0.10
For a gate, keep $1 USDC on Base in the credit wallet.