SDK & Agent Tools
Programmatic access for agents and scripts: the @ziptoken/mcp MCP server and @ziptoken/cli command-line tool cover ZipToken's public read-only data plane today. The TypeScript trading SDK further down this page is a separate, institutional surface (KYC, order placement) -- see the note before Installation.
@ziptoken/mcp
An MCP (Model Context Protocol) stdio server that exposes ZipToken's public, read-only data plane to AI agents: index values (Zillow ZHVI first prints), index markets, resolutions, geo search, reference quotes, and a local hedge-sizing calculator (plus an authenticated order write for trading-scope keys). It is a thin tool layer over the same REST API documented on the API Documentation page -- point it at ZIPTOKEN_API_URL and it works from anywhere. v1 ships stdio transport only; remote/HTTP transport is a documented follow-up.
Zillow attribution
Every response that surfaces an index value carries attribution: "Data from Zillow". The get_index_value tool always prints this line -- it is a Zillow data-license requirement, not cosmetic, and should not be dropped when relaying results to a user.
Tools
| Tool | Description |
|---|---|
| get_index_value | Current ZHVI value, YoY change, as-of month, and recent series for one geo node (state:TX, county:48201, city:tx-austin, zip:11215). |
| list_markets | List/filter ZipToken markets by zip, level, status, metric type, or metro area (paginated). |
| get_market | Full detail for one market by id (db UUID or frontend id like mkt-002). |
| get_resolution | Settlement outcome for a market: the Zillow first print it settled on, YES/NO outcome, dispute-window state. |
| search_geo | Free-text search over the geo hierarchy; returns canonical node ids for use in the other tools. |
| size_hedge | Pure local calculation -- sizes a strike-ladder hedge per the hedge-sizing methodology. No network call; caller supplies live strike prices (e.g. from list_markets). |
| get_quote | Reference RFQ price for a binary-trigger contract (node × strikeBps × expiry × notional): price, premium, max payout, and the seriesMarketId to accept against. The layered (deductible-based) kind quotes via GET /api/quotes directly -- see the call-out below. |
| place_order | Accept a quote by placing a BUY on outcome 0 -- an authenticated, side-effectful write that requires a trading-scope ZIPTOKEN_API_KEY. |
Graduated (layered) quotes -- the platform default
The quote tool's default contract for home value (ZHVI) and rent (ZORI) is now a graduated layer: a deductible (the index decline you absorb), a coverage limit, and a term, settling at coverage × min(1, max(0, (decline − deductible) / (exhaustion − deductible))). Quote it with GET /api/quotes?kind=layered&attachBps=&exhaustBps= (attach = the deductible in index-decline bps, exhaust = where the full limit pays). The response adds payoutKind, attachPct/exhaustPct, anyClaimProb/fullLimitProb (the model's chance of any payout / of the full limit), and the dollar readouts deductibleUsd = C·a/(x−a) and impliedExposureUsd. exhaustBps ≤ attachBps ⇒ 422 INVALID_LAYER; layered is zhvi/zori only in v1. Omitting kind keeps the legacy binary behavior, so the get_quote tool, the CLI quote command, and every existing strike-based integration keep working unchanged.
# 5% deductible, full limit at a 15% decline, $1,000,000 coverage limit
curl "$ZIPTOKEN_API_URL/api/quotes?node=county:37119&kind=layered\
&attachBps=500&exhaustBps=1500&expiry=2027-12¬ional=1000000"
# -> premiumUsd, anyClaimProb, fullLimitProb, deductibleUsd (= $500,000 here)Premium payment structures
The MCP server and CLI return a quote's reference premium — the model price. How that premium is settled is chosen at accept on the platform, not through these tools: in full at close, in installments (a down payment plus level monthly payments at an APR), or at funding / completion (the full premium plus a carry charge, deferred to the project milestone and owed regardless of outcome). Same coverage; only the payment timing differs.
Environment variables
| Var | Default | Notes |
|---|---|---|
| ZIPTOKEN_API_URL | https://zip-token-production.up.railway.app | Base URL of the ZipToken REST API. |
| ZIPTOKEN_API_KEY | (none) | Sent as x-api-key. Optional -- read routes work unauthenticated; a key raises the rate limit. |
Claude Desktop configuration
Add an entry to claude_desktop_config.json (Settings > Developer > Edit Config):
{
"mcpServers": {
"ziptoken": {
"command": "node",
"args": ["/absolute/path/to/zip_token/packages/mcp/dist/index.js"],
"env": {
"ZIPTOKEN_API_URL": "https://zip-token-production.up.railway.app",
"ZIPTOKEN_API_KEY": ""
}
}
}
}Omit ZIPTOKEN_API_KEY entirely (or leave it blank) if you don't have one yet -- read endpoints work without it. Any other MCP stdio client launches the server the same way: run node dist/index.js as a child process and set the two env vars.
Install & build
# From the repo root -- part of the ZipToken npm workspace monorepo
npm install
npm run build -w packages/mcp # -> packages/mcp/dist/index.js@ziptoken/cli
A command-line interface over the same read-only public API the MCP server exposes to agents -- for humans and scripts. Binary name: ziptoken. Index values, markets, resolutions, geo search, plus the local hedge-sizing calculator.
Install & build
npm install
npm run build -w packages/cli # -> packages/cli/dist/cli.js
# link the `ziptoken` binary for the workspace
npm link -w packages/cli
ziptoken index state:TXGlobal flags / environment variables
| Var | Flag | Default | Notes |
|---|---|---|---|
| ZIPTOKEN_API_URL | --api-url <url> | https://zip-token-production.up.railway.app | Base URL of the ZipToken REST API. |
| ZIPTOKEN_API_KEY | --api-key <key> | (none) | Sent as x-api-key. Optional -- read routes work unauthenticated; a key raises the rate limit. |
Global flags override env vars and must come before the subcommand, e.g. ziptoken --api-url http://localhost:4000 index state:TX.
ziptoken index <nodeId>
Current ZHVI value, YoY change, and as-of month for a geo node.
$ ziptoken index state:TX
Node: state:TX
Value: 312,400
YoY: 4.85%
As of: 2026-05
Source: zhvi_first_print
Data from Zillowziptoken markets list [--level] [--state] [--zip] [--status] [--limit]
List/filter markets, paginated.
$ ziptoken markets list --level county --state FL --limit 3
ID GEO DESCRIPTION STATUS PRICES
------- -------------- ---------------------------------------- -------- -----------------------
mkt-006 county:12086 Miami-Dade County ZHVI YoY below 3% ACTIVE YES 0.34 / NO 0.66
mkt-011 county:12057 Hillsborough County ZHVI level Q3 2026 ACTIVE YES 0.51 / NO 0.49
mkt-014 county:12099 Palm Beach County ZHVI YoY below 5% RESOLVED YES 1.00 / NO 0.00
Page 1 of 4 (total 11, hasMore=true)ziptoken markets get <id>
Full detail for one market by db id or frontend id.
$ ziptoken markets get mkt-002
ID: mkt-002
On-chain id: 0xabc123...
Geo node: zip:10001
Type: BINARY
Status: FINALIZED
Metric: median_sale_price
Threshold: 1300000
Resolves: 2026-06-30T00:00:00.000Z
Description: Will the ZHVI level for zip 10001 be at or above $1,300,000 in June 2026?
Outcomes:
[0] YES: 1
[1] NO: 0ziptoken resolution <marketId>
Structured resolution status: settlement terms, outcome, dispute-window state.
$ ziptoken resolution mkt-002
{
"marketId": "mkt-002",
"status": "FINALIZED",
"outcome": "YES",
"settledOnObservation": { "month": "2026-06", "value": 1341800 },
"finalizedAt": "2026-07-03T06:00:00.000Z"
}ziptoken geo search <query> [--level] [--state]
Free-text search over the geo hierarchy; returns canonical node ids.
$ ziptoken geo search austin --level city
ID LEVEL NAME STATE
-------------- ----- ------ -----
city:tx-austin city Austin TXziptoken hedge size --exposure --class --downside --fraction --price [--strikes] [--attach] [--beta-case]
Sizes a strike-ladder hedge per docs/research/hedge-sizing-methodology.md (proxy hedge ratios by asset class, digital-contract math, ladder weighting). Pure local calculation -- no network call; you supply live strike prices yourself (e.g. copied from ziptoken markets list). Asset classes: single-family, condo, sfr-btr, land, multifamily-value, multifamily-noi, office, and industrial (plus commercial shopping-center exposure).
$ ziptoken hedge size --exposure 10000000 --class single-family --downside 0.10 --fraction 0.5 --price 0.14
Asset class: single_family (beta_class=1, beta_geo=1, h_total=1.0000)
Hedge fraction: requested 50.0% -> capped 50.0%
Coverage target (C): $500,000
# STRIKE PRICE PRICE(EFF) WEIGHT COVERAGE CONTRACTS PREMIUM
- ------ ------- ---------- ------ -------- --------- --------
1 -10.0% $0.1400 $0.1414 100.0% $500,000 582,344 $82,343
Total contracts: 582,344
Total premium: $82,343 (0.82% of exposure)
Expected variance removed: ~43.8% (HE_mid=0.875 x F_capped=0.50)With no --strikes given and exactly one --price, the single strike defaults to --downside. --attach defaults to 0 if omitted; --beta-case (low/base/high) picks the β_class column when no override is given.
Development: npm run build -w packages/cli (tsc), npm run test -w packages/cli (vitest -- format helpers + hedge-arg parsing), npm run lint -w packages/cli (tsc --noEmit).
About the sections below
The rest of this page documents @ziptoken/sdk, a planned TypeScript client for the institutional Trading API (KYC, order placement, audit). It targets a different consumer (KYC'd trading accounts) than the MCP server and CLI above, which are for read-only agentic/programmatic access and are what has actually shipped for that use case today.
Installation
npm install @ziptoken/sdkThe SDK requires Node.js 18+ and TypeScript 5.0+.
import { kyc, trading, audit, liquidity } from '@ziptoken/sdk';Authentication
Configure the SDK with your API key before making requests. You can also set the base URL for development.
import { configure } from '@ziptoken/sdk';
configure({
apiKey: process.env.ZIPTOKEN_API_KEY,
baseUrl: 'https://api.ziptoken.io', // optional, this is the default
});Security
Never expose your API key in client-side code. Use environment variables and server-side calls only. The SDK is designed for Node.js server environments.
kyc
Identity verification and compliance.
createInquiry
createInquiry(tier: 'basic' | 'enhanced' | 'institutional'): Promise<KYCRecord>Start a KYC verification process for the specified tier.
const record = await kyc.createInquiry('enhanced');
console.log(record.status); // "pending"getStatus
getStatus(): Promise<KYCRecord>Get the current KYC verification status and limits.
const status = await kyc.getStatus();
if (status.status === 'approved') {
console.log('Verified at tier:', status.tier);
}getLimits
getLimits(): Promise<KYCLimits>Get the position and daily trading limits for your current tier.
const limits = await kyc.getLimits();
console.log('Max daily:', limits.maxDaily);trading
API key management and order placement.
createApiKey
createApiKey(label: string, permissions: string[], rateLimit?: number, expiresInDays?: number): Promise<ApiKey & { secret: string }>Create a new API key. The secret is returned only once.
const key = await trading.createApiKey(
'Bot v2',
['read:markets', 'create:orders', 'cancel:orders'],
300 // 300 req/min
);
// Store key.secret securelylistApiKeys
listApiKeys(): Promise<ApiKey[]>List all API keys (secrets are not included).
const keys = await trading.listApiKeys();revokeApiKey
revokeApiKey(keyId: string): Promise<{ success: boolean }>Permanently revoke an API key.
await trading.revokeApiKey('key_abc123');placeOrder
placeOrder(marketId: string, outcomeIndex: number, side: 'BUY' | 'SELL', amount: string, price: number, expiry?: number): Promise<TradingOrder>Place an order on an index-settled contract.
const order = await trading.placeOrder(
'mkt_10025_price',
0, // YES outcome
'BUY',
'500.00',
0.62 // limit price
);placeBatchOrders
placeBatchOrders(orders: OrderInput[]): Promise<TradingOrder[]>Place multiple orders atomically. All succeed or all fail.
const orders = await trading.placeBatchOrders([
{ marketId: 'mkt_10025_price', outcomeIndex: 0, side: 'BUY', amount: '200', price: 0.60 },
{ marketId: 'mkt_10036_price', outcomeIndex: 1, side: 'BUY', amount: '300', price: 0.45 },
]);cancelOrder
cancelOrder(orderId: string): Promise<{ success: boolean }>Cancel a single open order.
await trading.cancelOrder('order_xyz');cancelAllOrders
cancelAllOrders(marketId?: string): Promise<{ cancelledCount: number }>Cancel all open orders. Optionally filter by market.
const result = await trading.cancelAllOrders();
console.log('Cancelled:', result.cancelledCount);audit
Tamper-evident, hash-chained audit trail.
getLogs
getLogs(params?: { userId?: string; action?: string; severity?: string; limit?: number; offset?: number }): Promise<AuditEntry[]>Query audit logs with optional filters.
const logs = await audit.getLogs({
severity: 'critical',
limit: 100
});getStats
getStats(startTime?: number, endTime?: number): Promise<AuditStats>Get aggregated audit statistics over a time range.
const stats = await audit.getStats(
Date.now() / 1000 - 86400 * 7 // last 7 days
);verifyIntegrity
verifyIntegrity(): Promise<AuditIntegrity>Verify the hash chain integrity of the entire audit log.
const result = await audit.verifyIntegrity();
if (!result.valid) {
console.error('Chain broken at entry:', result.brokenAt);
}exportLogs
exportLogs(startTime?: number, endTime?: number): Promise<AuditEntry[]>Export audit logs for compliance reporting.
const logs = await audit.exportLogs(startTime, endTime);liquidity
Position limits, margin health, and fee management.
getTiers
getTiers(): Promise<LiquidityTier[]>List all liquidity tiers and their limits.
const tiers = await liquidity.getTiers();getTier
getTier(tier: string): Promise<LiquidityTier>Get a specific liquidity tier.
const tier = await liquidity.getTier('institutional');getRequirements
getRequirements(metricType: string): Promise<Record<string, unknown>>Get liquidity requirements for a specific metric type.
const reqs = await liquidity.getRequirements('zhvi');validatePosition
validatePosition(positionSize: number, marketId: string): Promise<{ valid: boolean; reason?: string }>Check if a proposed position size is within limits.
const check = await liquidity.validatePosition(50000, 'mkt_10025_price');
if (!check.valid) {
console.warn(check.reason);
}getMarginHealth
getMarginHealth(): Promise<MarginHealth>Get current account margin health status.
const health = await liquidity.getMarginHealth();
if (health.healthStatus === 'warning') {
// Reduce positions or add collateral
}getFeeEstimate
getFeeEstimate(amount: number, tier: string): Promise<FeeEstimate>Estimate trading fees for a given amount.
const est = await liquidity.getFeeEstimate(1000, 'standard');
console.log(est.feePercent); // "1.00%"getAlerts
getAlerts(limit?: number): Promise<Alert[]>Get liquidity and margin alerts.
const alerts = await liquidity.getAlerts(10);Type Reference
All types are exported from the SDK package and available for import.
import type { KYCRecord, TradingOrder, MarginHealth } from '@ziptoken/sdk';// Core Market Types
type MarketType = 'binary' | 'spread' | 'range'
type MarketCategory = 'price_threshold' | 'volume' | 'growth' | 'comparison'
type MarketStatus = 'active' | 'resolved' | 'pending'
// KYC
type KYCStatus = 'none' | 'pending' | 'approved' | 'rejected' | 'expired'
type KYCTier = 'none' | 'basic' | 'enhanced' | 'institutional'
interface KYCRecord {
status: KYCStatus
tier: KYCTier
sanctionsCleared: boolean
pepScreened: boolean
adverseMediaCleared: boolean
countryCode: string | null
expiresAt: number | null
limits: KYCLimits | null
}
interface KYCLimits {
maxPosition: number
maxDaily: number
maxTotal: number
}
// Trading
type ApiKeyPermission = 'read:markets' | 'read:portfolio' | 'create:orders' | 'cancel:orders' | 'read:oracle' | 'manage:custody'
interface ApiKey {
id: string
label: string
prefix: string
permissions: ApiKeyPermission[]
rateLimit: number
createdAt: number
lastUsed: number | null
expiresAt: number | null
}
interface TradingOrder {
id: string
maker: string
marketId: string
outcomeIndex: number
side: 'BUY' | 'SELL'
amount: string
price: number
status: 'OPEN' | 'FILLED' | 'PARTIALLY_FILLED' | 'CANCELLED'
filledAmount: string
expiry: number
createdAt: number
}
// Audit
interface AuditEntry {
id: string
action: string
userId: string
severity: 'info' | 'warning' | 'critical'
resource: string
details: Record<string, unknown>
success: boolean
timestamp: number
hash: string
previousHash: string
}
interface AuditStats {
totalEvents: number
byAction: Record<string, number>
bySeverity: Record<string, number>
timeRange: { start: number; end: number }
}
interface AuditIntegrity {
valid: boolean
totalEntries: number
checkedAt: number
brokenAt?: number
}
// Liquidity
interface LiquidityTier {
tier: string
maxPositionSize: number
maxDailyVolume: number
maxOpenPositions: number
marginRequirement: number
tradingFee: number
}
interface MarginHealth {
collateralBalance: string
totalExposure: string
marginHealth: number
frozen: boolean
healthStatus: 'healthy' | 'warning' | 'critical' | 'liquidation'
}
interface FeeEstimate {
fee: number
netAmount: number
amount: number
tier: string
feePercent: string
}Error Handling
All SDK methods throw on non-2xx responses. Wrap calls in try/catch for proper error handling.
import { trading } from '@ziptoken/sdk';
try {
const order = await trading.placeOrder(
'mkt_10025_price', 0, 'BUY', '100.00', 0.65
);
console.log('Order placed:', order.id);
} catch (error) {
if (error instanceof Error) {
console.error('API error:', error.message);
// Common error messages:
// "Insufficient balance"
// "Market is closed"
// "Position exceeds limit"
// "Invalid price range"
// "Rate limit exceeded"
}
}Rate Limiting
The API returns HTTP 429 when rate limits are exceeded. The SDK does not retry automatically. Implement exponential backoff in your application logic.
Network Errors
Network timeouts and connection failures throw standard Error objects. The message will contain the underlying fetch error description.
Validation Errors
Invalid parameters return HTTP 400 with a descriptive error message. Check parameter types and ranges before sending requests.