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"
}