Thassa.

Start here

Getting started

Everything you need to make your first authenticated call: one account, one key, one header.

One account, app + API

Thassa has a single user base shared by the app and the API. There is no separate developer signup: your API keys act as you, same identity, same wallet, same positions, same balance. An order placed via the API shows up in your app profile, and vice versa.

  • Sign up in the app. Accounts (and the embedded wallet that signs your orders) are created in the Thassa app, the API has no signup endpoint.
  • Keys inherit your identity. Anything gated for your user in the app (visibility, limits) applies identically through a key.
  • Orders are still yours to sign. The API is non-custodial: mutating calls carry payloads signed by your own wallet. A key alone can never move funds, see Gasless orders.

Create an API key

In the web app, open Settings → Developer and create a key. Pick a name and a scope:

ScopeGrants
readAuthenticated reads: your orders, positions, fills, balance; WebSocket subscriptions.
tradeEverything in read, plus mutations: placing and canceling orders.

The secret is shown once

The full key secret is displayed only at creation. Secrets are stored hashed (SHA-256); afterwards only a prefix is shown for identification. Lose it and you mint a new key.

Keys can also be managed programmatically, see API keys endpoints.

Authentication

Pass the key on every authenticated request in the X-Thassa-Key header:

curl
curl "http://localhost:8080/trade-api/v1/balance" \
  -H "X-Thassa-Key: $THASSA_KEY"

The WebSocket accepts the same header on connect, or (in browsers, which can’t set headers) the key via the Sec-WebSocket-Protocol header, see WebSocket. Keys are never passed as query parameters.

Two ways to trade

The trade API accepts two credentials, matching two ways of signing orders.

  • Live session (OAuth style). Send your Privy access token as Authorization: Bearer <token>. This is the route for agents and MCP-style integrations that hold a real login. The session acts as you with full scope, and your client signs orders locally, exactly like the app does.
  • API key with server-side signing. Turn on server-side signing in Settings, then submit orders with just X-Thassa-Key and no auth block. Thassa completes the order (nonce, expiry, max cost) and signs it through your delegated wallet. Without the opt-in, API keys can still trade by sending fully signed payloads.

Environments & base URLs

All examples in these docs are rendered against the configured base URL, currently http://localhost:8080. The docs site reads it from NEXT_PUBLIC_API_URL at build time, so a deployment can point every snippet at its own environment.

EnvironmentBase URLNotes
Developmenthttp://localhost:8080Local backend from the repo’s single-command dev environment (Postgres, MinIO, Anvil chain fork).
This deploymenthttp://localhost:8080Baked into every example on this page.

Route prefixes, by audience:

  • /trade-api/v1/…, the public trading API documented here (market data + authenticated trading).
  • /v1/developer/keys, key management (app-session authenticated).
  • /v1/ws, the WebSocket endpoint, which accepts API-key auth for market-data channels.

Rate limits

  • Public market-data endpoints are rate-limited by IP.
  • Authenticated endpoints are rate-limited per key. Order placement additionally enforces per-user rate limits and a maximum order size at the relayer gate.

Exceeding a limit returns 429 with the standard error envelope. Back off and retry; idempotency keys (below) make retries safe.

Response envelope

Successful responses wrap data under a named key, never a bare array. List endpoints paginate with ?cursor=&limit= and return next_cursor (absent or null on the last page). Pass it back verbatim to fetch the next page.

GET /trade-api/v1/markets, 200
{
  "markets": [
    { "id": "42", "question": "…", "status": "OPEN", "yes_price_cents": 62 }
  ],
  "next_cursor": "eyJvZmZzZXQiOjUwfQ"
}

Error format

Every error, 4xx or 5xx, is a single lowercase message under error:

401 Unauthorized
{ "error": "invalid api key" }
StatusMeaning
400Malformed request, bad ids, unknown fields, invalid payloads.
401Missing or invalid X-Thassa-Key.
403Key lacks the required scope (e.g. read key on a mutation), or the resource is gated.
404No such resource.
409Idempotency-key conflict (same key, different request).
429Rate limit exceeded.
500Server error, message shaped "failed to …".

Idempotency-Key

Every mutating endpoint accepts an Idempotency-Key header , a client-generated UUID. Semantics:

  • First request executes normally; the response is stored against (key, user).
  • Replay (same key, byte-identical request) returns the stored response without re-executing, a double-submitted order never double-spends.
  • Conflicting reuse (same key, different request hash) is rejected with 409.
idempotent order placement
curl -X POST "http://localhost:8080/trade-api/v1/orders" \
  -H "X-Thassa-Key: $THASSA_KEY" \
  -H "Idempotency-Key: 7d1f6f0a-3f2e-4a4e-9df1-2c9b3f6a1e42" \
  -H "Content-Type: application/json" \
  -d @order.json

Note

Always send an Idempotency-Key on POST /trade-api/v1/orders. Networks fail mid-flight; with the key, retrying is always safe.

Where next