Synpath
SDKs

TypeScript

A typed client generated from the server's OpenAPI document: place through cancel with no Python.

The server's OpenAPI document is the contract. Generate the types from it rather than writing them by hand, so a renamed field is a compile error instead of a runtime surprise. The routes are explicit, so every response is a real schema, not object.

Self-hosted. Everything on this page talks to your self-hosted server (synpath serve), which must be running. On the machine it runs on, the client reads its access token from ~/.synpath/servers.json; for a server elsewhere, set SYNPATH_SERVER and SYNPATH_ACCESS_TOKEN.

Installation

# 1. the contract, without starting a server
synpath schema --trading --out openapi.json

# 2. the types
npx openapi-typescript openapi.json --default-non-nullable false -o src/synpath.d.ts
--default-non-nullable false matters. Without it, every field with a default (time_in_force, post_only, reduce_only) is generated as required, and a plain limit order has to spell out three fields the server would fill in.

Quick Start

import type { components } from "./src/synpath.js";   // generated

type Order = components["schemas"]["Order"];
type OrderRequest = components["schemas"]["OrderRequest"];

const base = "http://127.0.0.1:8000/trading";         // your self-hosted server (synpath serve)
const headers = { authorization: `Bearer ${process.env.SYNPATH_ACCESS_TOKEN}`, "content-type": "application/json" };

async function call<T>(path: string, init: RequestInit = {}): Promise<T> {
  const res = await fetch(base + path, { ...init, headers: { ...headers, ...(init.headers ?? {}) } });
  if (!res.ok) {
    const { error } = await res.json();                  // { code, message, details }, on every route
    throw new Error(`${res.status} ${error.code}: ${error.message}`);
  }
  return (await res.json()) as T;
}

const request: OrderRequest = {
  market_id: "polymarket:2252244",
  side: "buy",           // buy takes YES, sell takes NO
  type: "limit",
  price: "0.42",         // always the YES price
  amount: "10",
  book: "typescript",
};

const placed = await call<Order>("/orders", { method: "POST", body: JSON.stringify(request) });
console.log(`placed ${placed.id} ${placed.side} ${placed.amount} at ${placed.price} (${placed.status})`);

Trading

type PageOfOrders = components["schemas"]["PageResponse_Order_"];

const open = await call<PageOfOrders>("/orders");                                   // list
const one = await call<Order>(`/orders/${placed.id}`);                              // get
const edited = await call<Order>(`/orders/${placed.id}`, {                          // amend
  method: "PATCH", body: JSON.stringify({ price: "0.41", amount: "8" }),
});
const canceled = await call<Order>(`/orders/${placed.id}`, { method: "DELETE" });   // cancel

Complex orders go to the same route with a different type and params; see Create complex order.

Portfolio

type Positions = components["schemas"]["PageResponse_Position_"];
type Fills = components["schemas"]["PageResponse_Fill_"];

const positions = await call<Positions>("/positions");
const fills = await call<Fills>("/fills?venue=polymarket");
const pnl = await call<components["schemas"]["PnlView"]>("/pnl?level=book");

Market Data

Market data on your server needs no token. Generate its types from the read contract, schema --out without --trading.

const read = "http://127.0.0.1:8000";
type Market = components["schemas"]["Market"];
type Book = components["schemas"]["OrderBook"];

const page = await (await fetch(`${read}/venues/polymarket/markets?query=election&limit=10`)).json();
const market: Market = await (await fetch(`${read}/venues/polymarket/markets/polymarket:2252244`)).json();
const book: Book = await (await fetch(`${read}/venues/polymarket/markets/polymarket:2252244/book?side=no`)).json();
console.log(market.yes.quote.ask, book.best_bid?.price);

Realtime (WebSockets)

The engine events socket replays from a sequence number, then stays live, so a reconnecting client misses nothing. Live market data from the venues is the Python streams' job today; see the WebSocket API reference.

import WebSocket from "ws";

const socket = new WebSocket(`ws://127.0.0.1:8000/trading/ws/events?key=${process.env.SYNPATH_ACCESS_TOKEN}&since=0`);
socket.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  console.log(event.seq, event.kind, event.key);     // order.accepted, fill.booked, ...
});

A complete round trip

clients/typescript/example.ts in the repository is the whole loop in about sixty lines: place, watch the event stream, cancel, confirm the account is flat. From that folder, against a server with a paper venue:

synpath schema --trading --out openapi.json
npm install
npm run types               # openapi.json -> src/synpath.d.ts
npm run build               # example.ts -> dist/example.js
npm start                   # reads the server's access token from ~/.synpath/servers.json
placed  paper-2 buy 5 at 0.35 (open)
open    1 order(s): paper-2
cancel  paper-2 → canceled
watched 12 events, including intent.planned, order.accepted, intent.sent, order.canceled
done    the account is flat, no Python was involved

Other generators

Any OpenAPI generator works from the same document. @hey-api/openapi-ts produces a client with functions rather than types alone; the same command gives Go, Java or Rust a client too.