Skip to main content

Authentication Flow

  1. Request Nonce message Obtain a message from the auth nonce endpoint
  2. Sign the message using SIWE The user signs the message with their wallet to prove ownership of the address.
  3. Exchange the signed message for a JWT access token Submit the signed message to receive a JWT access token.
  4. Use the token for all authenticated requests Include the JWT in the Authorization header for subsequent API calls.
  5. After completing authentication flow Deposite your funds on your contract address, use user details api to get contract address

Rate Limit

Global Rate Limit

Maximum 150 requests per second allowed

Order Creation Rate Limit

Maximum 10 orders per second allowed with networking slowdown api graph

Authentication API

Nonce API

Request Nonce: The client first requests a unique nonce from the backend. This nonce prevents replay attacks and must be included in the signed message. Endpoint POST /api/auth/nonce Response Type
type Response = {
  nonce?: string
  error?: string
}

Sign In API

Using the nonce from Step 1, the client creates a SIWE message and asks the wallet to sign it. Send the signed message details to the backend to authenticate the user. Endpoint POST /api/auth/signin Body
FieldTypeRequiredDescription
addressstringYesWallet address used to sign the message
signaturestringYesSignature generated by the wallet
issuedAtstring(ISO 8601)YesTimestamp when the message was signed
domainstringYesDomain requesting authentication
uristringYesURI of the requesting application
Response Type
type Response = {
  token: string
  is_firstTime: boolean
}
FieldTypeDescription
tokenstringJWT used for authenticated requests
is_firstTimebooleanIndicates if this is the user’s first login
SIWE SignIn Code Snippet
import { SiweMessage } from 'siwe'

const message = new SiweMessage({
  domain,
  address,
  statement: 'Welcome to Outcome market! Sign to connect.',
  uri,
  version: '1',
  chainId,
  nonce //from nonce api
})

const siweMessageString = message.prepareMessage()

Authenticated API Rules

All endpoints below require: Authorization: Bearer <token> Notes
  • userAddress is extracted from the JWT

Enable Trading

Enable Trading allows the outcome market smart contracts to move your tokens on your behalf. It’s a one-time approval that lets the platform spend ERC20 Tokens. Example Code
import { ethers } from 'ethers'

const provider = new ethers.BrowserProvider(window.ethereum)
const signer = await provider.getSigner()

const response = await axios.get('/api/enable-trading/signature', {
  headers: {
    Authorization: Bearer<token>
  }
})

const signature = await signer.signMessage(ethers.getBytes(messageHash))

const response = await axios.post(
  '/api/enable-trading',
  {
    signature
  },
  {
    headers: {
      Authorization: Bearer<token>
    }
  }
)

Enable Trading Signature

Endpoint GET /api/enable-trading/signature Response Type
type Response = {
  success: boolean
  messageHash: string
  error?: string
}

Enable Trade

Endpoint POST /api/enable-trading Body Parameters
NameTypeRequiredDescription
signaturestringYes
Example Response Response Type
type Response = {
  success: boolean
  data?: { transactionHash: string }
  error?: string
}

User Details API

Endpoint POST /api/me Response Type
type User = {
  id: string
  public_address: string
  username: string
  contract_address: string
  avatar_url: string
  created_at: string // ISO date string
  updated_at: string // ISO date string
  email: string | null
  auth_nonce: string | null
  order_nonce: number
  enable_trading_hash: string
  trading_enabled: boolean
}

Balance API

Get USER Balance

Endpoint GET /api/realtime/user/balance Example Response
{
  "balance": {
    "available": "1573",
    "locked": "0",
    "total": "1573"
  }
}
Balance Object Definition
FieldTypeDescription
availablestringOn Chain balance - Open orders amount
lockedstringOpen orders amount
totalstringOn Chain balance

Get Asset Balance

Endpoint GET /api/realtime/user/assetbalance Query Parameters
NameTypeRequiredDescription
marketIdnumberYesMarket ID
Example Response
{
  "balance": {
    "available": "1573",
    "locked": "0",
    "total": "1573"
  }
}
Asset Balance Object Definition
FieldTypeDescription
availablestringOn Chain Asset balance - Open orders amount
lockedstringOpen orders amount
totalstringOn Chain Asset balance

User Outcomes

Endpoint POST /api/me/outcomes Response Type
type Outcome = {
  id: number
  eventId: number
  outcome: string
  time: string // ISO 8601 timestamp
  contract: string
  tokenType: 'YES' | 'NO'
  price: string
  size: string
  pnl: string
  category: 'CRYPTO' | 'SPORTS'
  feedType: 'PYTH' | 'SBAN'
  action: 'CLOSED' | 'CLAIMED' | ''
  txnHash: string | null
  teamHash: string
  singleEvent: boolean
}
FieldTypeDescription
idnumberUnique identifier of the outcome record
eventIdnumberAssociated event ID
outcome"Won" | "Lose"Result of the position
timestring (ISO 8601)Event resolution timestamp
contractstringContract or market name
tokenType"YES" | "NO"Side/token held by the user
pricestringEntry price per token
sizestringQuantity of tokens
pnlstringProfit or loss for this outcome
actionstringuser claimed or not
txnHashstringclaimed txn hash

Claim Tokens API

Endpoint GET /api/markets/marketId/claim Response Type
type Response = {
  success: boolean,
  message: string,
  transaction: {
    hash: 0xstring,
  },
  error?: string
}
Market ID Convention
OutcomeMarket ID
YESEventID × 2
NOEventID × 2 + 1

Order APIs

Create Order

Place a new order. Rate Limit Maximum 10 orders per second allowed with networking slowdown Endpoint POST /api/orders Payload Type
type CreateOrderPayload = {
  market_id: string
  side: 'LIMIT' | 'MARKET'
  OrderSide: 'BUY' | 'SELL'
  quantity: string
  limit_price: string // required if OrderType == "LIMIT"
  asset: string
  slippage: string
}
Body Parameters
NameTypeRequiredDescription
market_idnumberYesYES = EventID × 2, NO = EventID × 2 + 1
quantitystringYesInteger quantity
limit_pricestringYesPrice per unit (0.0 - 1.0)
OrderSide"BUY" | "SELL"YesOrder side
OrderType"LIMIT" | "MARKET"YesOrder execution type
asset"YES" | "NO"YesOutcome asset (YES or NO)
slippagestringNoamount of slippage between 0 to 20 cents

Cancel Order

Endpoint POST /api/orders/orderId/cancel Parameters
NameTypeRequiredDescription
orderIdstringYesyour order id

Cancel All Open Orders

Endpoint POST /api/orders/cancel-all

Get Open Orders

Retrieve open and partially filled orders. Endpoint GET /api/realtime/openOrders Response
{
  success: boolean
  data: {
    orderId: string
    marketId: string
    quantity: string
    price: string
    side: string
    status: string
    created_at: string
  }
  ;[]
}

Position APIs

Get User Positions

Retrieve current user positions across all markets. Endpoint GET /api/realtime/positions Example Response Type
export type PositionsApiResponse = {
  success: boolean
  data: Position[]
}

export type Position = {
  contract: string
  size: string
  entry_price: string
  mark_price: string
  position_value: string
  pnl: string // e.g. "-117.98 (-34.83%)"
  side: 'YES' | 'NO'
  event_id: number
  teamName: string | null
  singleEvent: boolean
  category: 'STOCK' | 'CRYPTO' | 'SPORTS'
}
Position Object Definition
FieldTypeDescription
contractstringContract / market name
sizestringNet position size
entry_pricestringAverage entry price
mark_pricestring (optional)Current mark price
position_valuestringCurrent position value
pnlstringPnL formatted as value (percent)
side"YES" | "NO"Outcome side
event_idnumberEvent ID
teamNamestring | nullSports team (if applicable)
singleEventbooleanSingle-outcome market
categorystringCRYPTO, SPORTS, STOCK, COMMODITIES
Important Notes
  • mark_price and pnl may be "NaN" if pricing data is unavailable
  • YES and NO positions for the same event are returned separately

Trade History APIs

Get User Trade History

Retrieve paginated executed trades. Endpoint GET /api/realtime/tradeHistory Query Parameters
NameTypeDescription
userAddressstringuser wallet address
pagenumberDefault: 1
pageSizenumberDefault: 20, Max: 100
Trade Object Definition
FieldTypeDescription
idstringTrade ID
timestring (ISO 8601)Execution time
contractstringMarket name
directionstringe.g. Buy (Yes)
pricenumberExecution price
sizestringFilled quantity
tradeValuestringprice × size
teamNamestring | nullSports team
singleEventbooleanSingle-outcome
feestringFee paid
feeRatestringApplied fee rate
role"Maker" | "Taker"Liquidity role
closedPNLstringRealized PnL or ”—”
Notes
  • price may include floating-point artifacts
  • closedPNL is populated only when a trade reduces exposure

Public Market Data APIs

Get Orderbook

Retrieve raw, non-aggregated orderbook data. Endpoint GET /api/realtime/orderbook/:marketId Authentication: Not required Market ID Convention
OutcomeMarket ID
YESEventID × 2
NOEventID × 2 + 1
Orderbook Response Structure
{
  "bids": [Order],
  "asks": [Order],
  "_meta": Meta
}
Order Object
FieldTypeDescription
orderIdstringUnique order ID
pricestringLimit price
quantitystringRemaining quantity
userIdstringMaker wallet
sourcestringLiquidity source
Metadata (_meta)
FieldTypeDescription
dollarCapnumberMax dollar exposure
syntheticEnabledbooleanSynthetic liquidity flag
syntheticNotionalnumber | nullSynthetic size
timestampnumberServer timestamp (ms)
Notes
  • Orders are not aggregated
  • Best bid = highest bid price
  • Best ask = lowest ask price

Event-Level Market Data

Events API

Endpoint GET /api/realtime/events?page=1&pageSize=10&status=OPEN&sortBy=volume Response Type
type Event = {
  id: number
  title: string
  description: string
  category: 'CRYPTO' | 'STOCK' | 'SPORTS'
  subcategory: string
  status: 'OPEN' | 'RESOLVED' | 'CLOSED'
  image_url: string
  tags: string
  created_at: string // ISO 8601
  updated_at: string // ISO 8601
  start_time: string | null
  end_time: string
  rules: string
  nickname: string
  autoResolve: boolean
  isMultiEvent: boolean
  isPrimaryEvent: boolean
  singleEvent?: boolean
  operator: 'LT' | 'GT' | null
  triggerPrice: string | null
  pythPriceId: string | null
  feedType: 'PYTH' | 'SBAN'
  priceSymbol: string
  provider: string | null
  providerEventId: string | null
  winningTokenId: number | null
  txnHash: string | null
  outcome: string | null
  resolutionHash: string | null
  resolutionTime: string | null
  resolutionReason: string | null
  leagueName: string | null
  matchStatus: string | null
  sportId: string | null
  teamA: string | null
  teamA_id: string | null
  teamA_image: string | null
  teamB: string | null
  teamB_id: string | null
  teamB_image: string | null
  markets?: Market[]
  yesMarketId?: number
  noMarketId?: number
  yesMarketPrice?: string
  noMarketPrice?: string
  yesPercent?: number
  noPercent?: number
  chance: string
  priceVariation: string
  volume: number
  totalVolume: number
  comment_count: number
  isWatchlisted: boolean
}

export type Market = {
  eventId: number
  title: string
  status: string
  end_time: string
  rules: string
  marketIds: number[]
  team_id: string
  teamName: string
  teamImage: string | null
  seedOperator: string
  chance: string
  yesMarketPrice: string
  noMarketPrice: string
  winningTokenId: number | null
  outcome: string | null
  volume: number
  priceVariation: string
}
Query Parameters
ParameterTypeDescription
categorystringFilter events by category (e.g. CRYPTO, STOCK, SPORTS)
statusstringFilter events by status (e.g. OPEN, RESOLVED, CLOSED)
subcategorystringFilter events by subcategory (e.g. Hype, MEMES, SOCCER)
searchstringText search on event title or description
addressstringWallet address to personalize results (e.g. watchlisted events)
pagenumberPage number for pagination
pageSizenumberNumber of records per page
sortBystringField used for sorting (e.g. volume, end_time, created_at)
sortOrder"asc" | "desc"Sort direction
leagueNamestringFilter sports events by league name
volumenumberMinimum total volume filter

Get Event By Id

Endpoint GET /api/realtime/events/:eventId Response Type
type Event = {
  id: number
  title: string
  description: string
  category: 'CRYPTO' | 'STOCK' | 'SPORTS'
  subcategory: string
  status: 'OPEN' | 'RESOLVED' | 'CLOSED'
  image_url: string
  tags: string
  created_at: string // ISO 8601
  updated_at: string // ISO 8601
  start_time: string | null
  end_time: string
  rules: string
  nickname: string
  autoResolve: boolean
  isMultiEvent: boolean
  isPrimaryEvent: boolean
  singleEvent?: boolean
  operator: 'LT' | 'GT' | null
  triggerPrice: string | null
  pythPriceId: string | null
  feedType: 'PYTH' | 'SBAN'
  priceSymbol: string
  provider: string | null
  providerEventId: string | null
  winningTokenId: number | null
  txnHash: string | null
  outcome: string | null
  resolutionHash: string | null
  resolutionTime: string | null
  resolutionReason: string | null
  leagueName: string | null
  matchStatus: string | null
  matchStatus: string | null
  sportId: string | null
  teamA: string | null
  teamA_id: string | null
  teamA_image: string | null
  teamB: string | null
  teamB_id: string | null
  teamB_image: string | null
  markets?: Market[]
  yesMarketId?: number
  noMarketId?: number
  yesMarketPrice?: string
  noMarketPrice?: string
  yesPercent?: number
  noPercent?: number
  chance: string
  priceVariation: string
  volume: number
  totalVolume: number
  comment_count: number
  isWatchlisted: boolean
}

Get Sports Event By Id

Endpoint GET /api/realtime/sports/:eventId Response Type
SportsEvent {
  title: string;
  description: string;
  category: "SPORTS";
  subcategory: "SOCCER";
  status: "OPEN" | "PAUSED" | "RESOLVED";
  markets: Market[];
  created_at: ISODateString;
  updated_at: ISODateString;
  tags: string;
  image_url: string;
  autoResolve: boolean;
  operator: string | null;
  pythPriceId: string | null;
  triggerPrice: number | null;
  winningTokenId: string | null;
  txnHash: string;
  resolutionHash: string | null;
  start_time: ISODateString | null;
  matchStartTime: ISODateString;
  matchStatus: "NOT_STARTED" | "LIVE" | "FINISHED";
  resolutionTime: ISODateString | null;
  feedType: "SBAN";
  priceSymbol: string;
  provider: "NONN";
  sportId: string;
  leagueName: string;
  comments: Comment[];
  comment_count: number;
  is_watchlisted: boolean;
  totalVolume: string;
  priceVariation: string;
  yesPercent: number;
  noPercent: number;
}

Market {
  eventId: number;
  title: string;
  nickname: string;
  end_time: ISODateString;
  rules: string;
  marketIds: number[];
  team_id: string;
  teamName: string;
  teamImage: string;
  seedOperator: "TEAMA" | "TEAMB" | "DRAW";
  chance: string;
  yesMarketPrice: string;
  noMarketPrice: string;
  volume: number | string;
  winningTokenId: string | null;
  outcome: "YES" | "NO" | null;
  priceVariation: string;
}

Get Event Trades

Retrieve recent trades across all markets in an event. Endpoint GET /api/realtime/sports/:eventId Response Type
type TradesApiResponse = {
  success: boolean
  data: {
    trades: {
      id: string
      user: {
        id: string
        name: string
      }
      choice: 'YES' | 'NO'
      side: 'BUY' | 'SELL'
      price: number
      size: number
      time: string
    }[]
    totalCount: number
  }
  cached: boolean
}

Get Sports Event Trades

Retrieve recent trades across all markets in a sports event. Endpoint GET /api/realtime/sports/:eventId/trades Response Type
type TradesApiResponse = {
  success: boolean
  data: {
    trades: {
      id: string
      user: {
        id: string
        name: string
      }
      choice: 'YES' | 'NO'
      side: 'BUY' | 'SELL'
      price: number
      size: number
      time: string
    }[]
    totalCount: number
  }
  cached: boolean
}

Withdraw Funds

Example Code
import { ethers } from 'ethers'

// encode withdrawal calldata
const iface = new ethers.Interface(['function transfer(address recipient, uint256 amount)'])

const amountInWei = ethers.parseUnits(amount, 6)
const data = iface.encodeFunctionData('transfer', [recipient, amountInWei])

// create message hash
const messageHash = ethers.solidityPackedKeccak256(
  ['address', 'uint256', 'address', 'uint256', 'bytes', 'uint256'],
  [userContractAddress, BigInt(chainId), USDT_ADDRESS, 0, data, nonce]
)

// sign message
const signature = await signer.signMessage(ethers.getBytes(messageHash))

const response = await axios.post(
  '/api/me/withdraw',
  {
    amount,
    recipient,
    signature
  },
  {
    headers: {
      Authorization: `Bearer ${token}`
    }
  }
)
Endpoint POST /api/me/withdraw Body
NameTypeRequiredDescription
amountstringYesamount of tokens to withdraw
recipientstringYesYour wallet EOA Address
signaturestringYesSigned Message