Thassa.

API reference

Trading (authenticated)

All endpoints here require X-Thassa-Key; mutations require scope trade and honor Idempotency-Key. Order placement goes through the same relayer gate as the app — same validation, same batching, same states.

Place an order

POST/trade-api/v1/ordersX-Thassa-Key · scope trade

Accepts the same non-custodial payload as the app: the order fields plus an EIP-3009 authorization whose auth_nonce equals the order’s EIP-712 digest (the signature carriage convention). The API never signs for you — it validates and relays.

Maker binding

order.maker MUST equal the wallet registered to the API key’s user. Orders signed by any other address are rejected.
Body fieldTypeDescription
order.market_idstringrequiredTarget market. New-market opening orders are signed with marketId = 0 and bound to the assigned id on creation.
order.sideintrequired0 = YES, 1 = NO.
order.priceintrequiredLimit price in cents, 1..99.
order.sharesstringrequiredNumber of $1 shares.
order.max_coststringrequiredMax token base units the signature authorizes (escrow + taker-fee headroom).
order.affiliate_post_idstringrequiredPost routing the trade; "0" = none.
order.expiryintrequiredUnix seconds; order invalid after this.
order.noncestringrequiredPer-maker sequential nonce.
order.makeraddressrequiredYour registered wallet.
auth.valuestringrequiredEIP-3009 value; must cover max_cost.
auth.valid_after / valid_beforestringrequiredAuthorization validity window (unix seconds).
auth.auth_noncebytes32requiredMUST equal orderDigest(order). Validated before batching.
auth.v / r / ssigrequiredThe single EIP-3009 signature.
request body
{
  "order": {
    "market_id": "42",
    "side": 0,                      // 0 = YES, 1 = NO
    "price": 62,                    // cents, 1..99
    "shares": "100",
    "max_cost": "63700000",         // token base units: escrow + fee headroom
    "affiliate_post_id": "0",
    "expiry": 1752969600,           // unix seconds
    "nonce": "7",                   // per-maker sequential
    "maker": "0x4A1f…9E02"          // MUST be your registered wallet
  },
  "auth": {                          // EIP-3009 ReceiveWithAuthorization
    "value": "63700000",
    "valid_after": "0",
    "valid_before": "1752969600",
    "auth_nonce": "0x83fa…11cd",    // = orderDigest(order) — signature carriage
    "v": 27,
    "r": "0x5c02…aa19",
    "s": "0x2e8d…03b7"
  }
}
place-order.ts (viem, complete)
import { createWalletClient, hashTypedData, http, parseSignature } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { randomUUID } from "node:crypto";

const account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);
const wallet = createWalletClient({ account, transport: http(RPC_URL) });

// 1. Build the order. nonce is per-maker sequential — read it from
//    GET /trade-api/v1/balance (order_nonce) or the contract's nonces(maker).
const order = {
  marketId: 42n,
  side: 0,                             // 0 = YES
  price: 62,                           // cents
  shares: 100n,
  maxCost: 63_700_000n,                // 100 × $0.62 escrow + fee headroom (6-dp token)
  affiliatePostId: 0n,
  expiry: BigInt(Math.floor(Date.now() / 1000) + 3600),
  nonce: 7n,
  maker: account.address,
};

// 2. Compute the order's EIP-712 digest — domain {ThassaMarkets, 1, chainId, contract}.
const orderDigest = hashTypedData({
  domain: {
    name: "ThassaMarkets",
    version: "1",
    chainId: CHAIN_ID,
    verifyingContract: MARKETS_CONTRACT,
  },
  types: {
    Order: [
      { name: "marketId",        type: "uint256" },
      { name: "side",            type: "uint8"   },
      { name: "price",           type: "uint8"   },
      { name: "shares",          type: "uint80"  },
      { name: "maxCost",         type: "uint256" },
      { name: "affiliatePostId", type: "uint256" },
      { name: "expiry",          type: "uint64"  },
      { name: "nonce",           type: "uint256" },
      { name: "maker",           type: "address" },
    ],
  },
  primaryType: "Order",
  message: order,
});

// 3. Sign ONE thing: the payment token's ReceiveWithAuthorization,
//    with nonce = orderDigest (the signature carriage convention).
const signature = await wallet.signTypedData({
  domain: {
    name: PAYMENT_TOKEN_NAME,          // the token's own EIP-712 domain
    version: PAYMENT_TOKEN_VERSION,
    chainId: CHAIN_ID,
    verifyingContract: PAYMENT_TOKEN,
  },
  types: {
    ReceiveWithAuthorization: [
      { name: "from",        type: "address" },
      { name: "to",          type: "address" },
      { name: "value",       type: "uint256" },
      { name: "validAfter",  type: "uint256" },
      { name: "validBefore", type: "uint256" },
      { name: "nonce",       type: "bytes32" },
    ],
  },
  primaryType: "ReceiveWithAuthorization",
  message: {
    from: account.address,
    to: MARKETS_CONTRACT,              // always the markets contract
    value: order.maxCost,
    validAfter: 0n,
    validBefore: BigInt(order.expiry),
    nonce: orderDigest,
  },
});
const { v, r, s } = parseSignature(signature);

// 4. Submit — idempotently.
const res = await fetch("http://localhost:8080/trade-api/v1/orders", {
  method: "POST",
  headers: {
    "X-Thassa-Key": process.env.THASSA_KEY!,
    "Idempotency-Key": randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    order: {
      market_id: order.marketId.toString(),
      side: order.side,
      price: order.price,
      shares: order.shares.toString(),
      max_cost: order.maxCost.toString(),
      affiliate_post_id: order.affiliatePostId.toString(),
      expiry: Number(order.expiry),
      nonce: order.nonce.toString(),
      maker: order.maker,
    },
    auth: {
      value: order.maxCost.toString(),
      valid_after: "0",
      valid_before: order.expiry.toString(),
      auth_nonce: orderDigest,
      v: Number(v),
      r,
      s,
    },
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const { order: placed } = await res.json(); // status: "QUEUED"
201 response
{
  "order": {
    "id": "0f6b1c9a-88a1-4c5e-b7d2-6f6a5f3f2e11",
    "market_id": "42",
    "side": "yes",
    "price_cents": 62,
    "shares": "100",
    "filled_shares": "0",
    "status": "QUEUED",
    "created_at": "2026-07-16T09:12:30Z"
  }
}

The response is immediate with status QUEUED; the relayer lands the batch onchain seconds later and the order moves to RESTING, PARTIAL, or FILLED. Track it via GET /trade-api/v1/orders or the WebSocket.

Cancel an order

DELETE/trade-api/v1/orders/{id}X-Thassa-Key · scope trade

Cancels a resting order (signed cancel, relayed like placement). The unfilled remainder is refunded to your free balance; already-filled shares stay filled.

curl
curl -X DELETE "http://localhost:8080/trade-api/v1/orders/0f6b1c9a-88a1-4c5e-b7d2-6f6a5f3f2e11" \
  -H "X-Thassa-Key: $THASSA_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

List your orders

GET/trade-api/v1/ordersX-Thassa-Key · scope read
Query parameterTypeDescription
marketstringoptionalFilter to one market id.
cursorstringoptionalOpaque pagination cursor.
limitintoptionalPage size (server-capped).
curl
curl "http://localhost:8080/trade-api/v1/orders?market=42" \
  -H "X-Thassa-Key: $THASSA_KEY"
200 response
{
  "orders": [
    {
      "id": "0f6b1c9a-88a1-4c5e-b7d2-6f6a5f3f2e11",
      "market_id": "42",
      "side": "yes",
      "price_cents": 62,
      "shares": "100",
      "filled_shares": "40",
      "status": "PARTIAL",
      "created_at": "2026-07-16T09:12:30Z"
    }
  ],
  "next_cursor": null
}

List your positions

GET/trade-api/v1/positionsX-Thassa-Key · scope read

Net position per market and side, with average entry price and realized PnL (in token base units).

curl
curl "http://localhost:8080/trade-api/v1/positions" \
  -H "X-Thassa-Key: $THASSA_KEY"
200 response
{
  "positions": [
    {
      "market_id": "42",
      "question": "Will it rain in San Francisco on Saturday?",
      "market_status": "OPEN",
      "side": "yes",
      "shares": "40",
      "avg_price_cents": 62,
      "realized_pnl": "0"
    }
  ],
  "next_cursor": null
}

List your fills

GET/trade-api/v1/fillsX-Thassa-Key · scope read

Your execution history across markets — every match your orders participated in, with price, shares, fee, and transaction hash.

curl
curl "http://localhost:8080/trade-api/v1/fills" \
  -H "X-Thassa-Key: $THASSA_KEY"
200 response
{
  "fills": [
    {
      "id": "f7a2…",
      "market_id": "42",
      "order_id": "0f6b1c9a-88a1-4c5e-b7d2-6f6a5f3f2e11",
      "side": "yes",
      "price_cents": 62,
      "shares": "40",
      "fee": "659680",
      "tx_hash": "0x8c31…e9d0",
      "created_at": "2026-07-16T09:12:34Z"
    }
  ],
  "next_cursor": null
}

Get your balance

GET/trade-api/v1/balanceX-Thassa-Key · scope read

Payment-token balance of your registered wallet, plus your next sequential order nonce.

curl
curl "http://localhost:8080/trade-api/v1/balance" \
  -H "X-Thassa-Key: $THASSA_KEY"
200 response
{
  "balance": "241503300",
  "wallet_address": "0x4A1f…9E02",
  "order_nonce": "8"
}