For autonomous callers

An order book
your agent can reach
on its own.

Prediction, scalar and spot markets behind one REST API, with discovery that needs no key, credentials that need no browser, and refusals a program can act on.

Every call on this page is public or reachable with a credential the agent can obtain itself. Nothing here needs a person at a keyboard except the one moment that should: the second an agent asks for its own environment.

Give this to your agent
You can trade on Clobber, a hosted central limit order book.

Read https://clobberhq.com/llms.txt first. It is written for you,
not for a person, and it carries both ways in:

  1. The public demo. No key, no signup, play money, real engine.
     GET https://dashboard.clobberhq.com/demo/api/market-data
  2. Your own sandbox. Ask for a device code, print it for me,
     and poll until I approve it. I will type it once. That key
     creates currencies, markets and accounts: you can run the
     venue, not just trade on it.

Prices and quantities are decimal strings at the market's tick
and lot size. Send an Idempotency-Key on every write. A refusal
carries a machine readable code: read the code, not the prose.
Paste it into a system prompt, a tool description, or a README the agent reads.

Two ways in

One of them needs nothing from anybody

An agent that has to ask its owner for an account, a form and a dashboard login before it can read a price is an agent that stops. Start it where nothing is asked, and let it earn its own environment later.

Rung one

Trade the demo, no key

A real environment on a real cell, running the engine production runs, with play money, a daily faucet and bots quoting both sides so an order can actually cross.

Discover, then take an identity
# the open markets, one per kind, and the feed address
curl -s https://dashboard.clobberhq.com/demo/api/market-data

# an identity of your own choosing, kept by its anchor
curl -s -X POST https://dashboard.clobberhq.com/demo/api/token \
  -H "Content-Type: application/json" \
  -d '{"anchor":"a-unique-string-you-keep"}'

# the day's money, then an order
curl -s -X POST https://dashboard.clobberhq.com/demo/api/faucet \
  -H "Authorization: Bearer $TOKEN"

The same anchor always answers with the same account, which is what makes a daily faucet worth having. One claim a day, thirty orders a minute per identity.

Rung two

Your own sandbox, no browser of yours

Your own currencies, markets and accounts, on the identical engine. The device authorization grant, the same shape a terminal tool uses for its own login: your human types one code into github.com, and nothing else.

Ask, print, poll
# no credential, because you have none yet
curl -s -X POST https://dashboard.clobberhq.com/api/agent/device
=> {"verification_uri":..., "user_code":"ABCD-1234",
    "device_code":..., "interval":5, "instructions":...}

# print instructions verbatim, then poll every interval
curl -s -X POST https://dashboard.clobberhq.com/api/agent/token \
  -H "Content-Type: application/json" \
  -d '{"device_code":"...","name":"my-sandbox","accept_terms":true}'

While nobody has approved it, the answer is 202 carrying authorization_pending or slow_down: neither is an error, and neither needs handling beyond waiting. The approval answers once, with the key, the REST base URL and the feed address. Store it before you do anything else.

Discovery

How a program finds a price

One unauthenticated GET answers what is open, what it is priced in, and where the live prices come from. Nothing is scraped, nothing is guessed, and no symbol needs hardcoding.

GET /demo/api/market-data
{
  "markets": [
    { "symbol": "RAIN-BA-0914", "kind": "binary",
      "status": "open", "tick_size": "0.01",
      "min_price": "0.00", "max_price": "1.00",
      "settlement_currency": "PLAY" },
    { "symbol": "TEMP-BA-0914", "kind": "scalar", ... },
    { "symbol": "GEMS-PLAY",   "kind": "pair",   ... }
  ],
  "feed": { "url": "wss://feed-sandbox.clobberhq.com",
             "key": "pk_test_..." }
}
the symbolsCarry the day the market was opened, so an agent reads them here on every run instead of holding one from last week.
the gridTick size and lot size are the only prices and sizes the engine will accept. A price off the grid is refused before it reaches the book.
the feedThe same answer hands you the WebSocket address and a publishable key, which is safe to hold in a client: it reads public channels and can place nothing.
your ownIn your environment the same reading is GET /v1/markets with your key, and it carries the same fields.

Live prices arrive as a snapshot and then deltas, with a sequence number per market that never skips. A gap means you missed one: resubscribe and the snapshot re-anchors you. That rule is the feed's whole contract, and it is written down.

What a price means, per kind

KindA price isSettlementWhat an agent does with it
binary The probability the market puts on an outcome, between 0.01 and 0.99. One contract pays 1.00 of the settlement currency if the outcome happens, 0 if it does not. Read it as a forecast, or take the other side of one. A buyer posts price x qty; a short seller posts (1 - price) x qty.
scalar The number itself, inside a stated range: a temperature, a count, a percentage. Pays the resolved value, normalized inside the range the market declared. A point estimate with a book behind it, which is a distribution you can actually trade against.
pair An ordinary spot price: one asset quoted in another. Nothing to resolve. You hold what you sell, and the trade moves both sides. Swap credits, items, compute hours or tokens at a price two parties agreed on.

Three kinds, one engine, one matching rule: strict price time priority, integer arithmetic, and a double entry ledger inside the engine that is conserved on every command. The whitepaper has the mechanism, and the integrity report has the checks.

Tool surface

Four tools is the whole job

An agent does not need the API. It needs a handful of tools whose arguments are the fields the engine already takes, and an execution path that is safe to retry.

list_markets () -> [{symbol, kind, tick_size, lot_size, status}]

What exists right now. Call it on every run, never cache a symbol between them.

get_book (market, depth) -> {bids, asks, last}

The touch and the depth behind it, so a price the agent picks is a price that exists.

place_order (market, side, type, price, qty, time_in_force)

Returns once the engine has processed it, so the answer already carries the fills.

get_balances () -> [{currency, total, held, available}]

Total, held and available, separately. Held is what open orders have reserved.

Cancelling is DELETE /v1/orders/{id}, and there is a panic button, POST /v1/orders/cancel_all, worth wiring to whatever kill switch an autonomous caller runs behind.

place_order, as a tool definition
{
  "name": "place_order",
  "description": "Place an order on a Clobber market.",
  "input_schema": {
    "type": "object",
    "properties": {
      "market": {"type": "string"},
      "side":   {"enum": ["buy", "sell"]},
      "type":   {"enum": ["limit", "market"]},
      "price":  {"type": "string"},
      "qty":    {"type": "string"},
      "time_in_force": {"enum": ["gtc", "ioc", "fok"]}
    },
    "required": ["market", "side", "type", "qty"]
  }
}
decimal stringsPrices and quantities are strings, at the market's tick and lot. A float that rounds is a price the engine refuses, and it refuses it with a code that names the grid.
the retrySend the tool call's own id as Idempotency-Key. A repeat returns the recorded answer for 24 hours instead of placing a second order, which makes a resumed run safe by construction.
the keyAn account key is scoped to one account: it can place and cancel for that account and read its balances, and it cannot see another one. That is the credential an autonomous caller should hold.

Run the venue

Or be the market, not a caller into one

The key rung two hands over is an operator's key. Four calls open a market, one closes it, and everything in between is yours: your currency, your symbols, your users' accounts, your screen.

An empty environment to a settled market
# 1. a settlement currency, at the scale you choose
POST /v1/currencies
     {"code": "USDC", "scale": 6}

# 2. a market: binary, scalar or pair
POST /v1/markets
     {"symbol": "WILL-IT-RAIN-BA", "kind": "binary",
      "tick_size": "0.01", "settlement_currency": "USDC"}

# 3. an account per end user of yours, funded
POST /v1/accounts
     {"reference": "user_alice"}
POST /v1/accounts/acc_.../credits
     {"currency": "USDC", "amount": "1000.00"}

# 4. their orders, on their behalf
POST /v1/orders          Clobber-Account: acc_...
     {"market": "WILL-IT-RAIN-BA", "side": "buy",
      "type": "limit", "price": "0.61", "qty": "250"}

# 5. the outcome, and every position settles at once
POST /v1/markets/WILL-IT-RAIN-BA/resolve
     {"outcome": "yes"}
your currencyYou pick the code and the scale. Inside the engine every amount is an integer in minor units, and the decimal strings are the boundary, which is why nothing here ever rounds.
your marketsBinary pays 1.00 to the winning side, scalar pays the resolved number normalized inside the range you declared, pair is spot and never resolves. One engine, one matching rule, three shapes.
your usersOne account each, carrying your own identifier as its reference, so you do not keep a mapping table. POST /v1/accounts/{id}/keys mints that user an account key their own client can hold.
your screenThe environment's publishable key, or a fifteen minute feed token per market, and the components draw your book, your depth, your tape.
the endingOne command declares the outcome and settles every position atomically. void is the honest ending when an event cannot be adjudicated: everyone gets back exactly what they paid and the fees already charged are reversed.
told, not polledRegister a webhook and the fills, the settlements and the lifecycle come to you, with per registration retries and the state of every delivery readable.

About fifteen minutes from an empty environment to a market that has traded and settled, and the same sequence runs as an integration test on every commit, which is the only reason that sentence is allowed on this page. A sandbox carries plan ceilings on markets, accounts and open orders, and a refusal names the one it hit rather than making you guess. Production is opened with a person: a real book with real money is a conversation, not a signup form.

The human in the loop

Somebody still wants to watch it happen

The same order fields a tool call fills in are what a ticket, a chat bot or a wallet signature fill in, and the same feed an agent reads is what draws the screen a person watches. The components are a package, and the order channels are a section of its gallery.

A live book, two tags, no build
<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/@clobber/ui/dist/clobber-ui.css">
<script src="https://cdn.jsdelivr.net/npm/@clobber/ui"></script>
<div id="market"></div>
<script>
  ClobberUI.mountMarket({
    target: "#market",
    url: "wss://feed-sandbox.clobberhq.com",
    credential: { publishableKey: "pk_test_..." },
    market: "RAIN-BA-0914",
  });
</script>

Order book, depth, candles, trade tape, venue ticker and a headless order ticket, as plain DOM with React wrappers over the same widgets. Every one of them is live in the gallery, in four skins, with the code beside it.

RAIN-BA-0914 binary Illustrative
BidQty
AskQty
Simulated on a fixed seed. Not a live market. last 0.61 / seq 48,210

The gallery's order channels section runs five surfaces that are not the ticket, all placing through the same adapter: a text message, two chat bots, a browser wallet and an agent with a place_order tool. What changes per channel is which field names the sender and which one is safe as an idempotency key.

Guardrails

What keeps an autonomous caller honest

An agent retries, resumes, runs twice by accident and asks for things that cannot happen. None of that is a reason to be careful with it. It is a reason for the venue to be built so that carelessness is bounded.

holdsPlacing an order takes a hold on the balance it needs. An account cannot spend what it has already committed, and a book cannot fill an order the ledger has not reserved.
conservedThe double entry ledger lives inside the engine, integer only, and every command leaves it balanced. There is no reconciliation job because there is nothing to reconcile.
idempotencyA retried write returns its first answer for 24 hours. Two identical tool calls are one order.
refusalsEvery failure carries a machine readable code, a human message and a request id. A caller branches on the code and never parses prose.
ceilingsToken buckets, with the order path given the most room. Every response names the binding bucket and the seconds until it refills, and a 429 carries Retry-After: honour it, with jitter. The demo adds its own, one faucet claim a day and thirty orders a minute per identity.
blast radiusAn account key reaches one account. A publishable key reads public channels and writes nothing. The keys that can reach every account stay on a server.
A refusal is an instruction
{
  "error": {
    "code": "price_not_on_tick",
    "message": "price must be a multiple of 0.01",
    "param": "price",
    "request_id": "req_..."
  }
}

The error catalog lists every code with its status and what to do about it. It is the part of an API an agent actually lives in.

Start

One call, no credential, right now

Read what is open, take an identity, claim the faucet, place an order. Then ask for a code and get an environment of your own.

The first line
curl -s https://dashboard.clobberhq.com/demo/api/market-data