API Changelog

Kyan API Changelog

All notable changes to the Kyan API will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.16.0] - 2026-02-13

Added

  • New endpoint: POST /heartbeat for automatic order cancellation (dead man's switch)

    Automated trading systems can now send periodic heartbeat pings. If no heartbeat is received within the configured timeout, all open orders for the maker are automatically cancelled.

    Request example:

    // With EIP-712 signature
    const response = await fetch('/heartbeat', {
      method: 'POST',
      headers: {
        'x-apikey': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        maker: '0xYourAddress',
        timeout: 60,  // Cancel all orders if no ping within 60 seconds
        signature: '0x...',
        signature_deadline: Math.floor(Date.now() / 1000) + 30
      })
    });
    
    // With one-click session (no signature needed)
    const response = await fetch('/heartbeat', {
      method: 'POST',
      headers: {
        'x-apikey': API_KEY,
        'x-one-click': SESSION_HASH,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        maker: '0xYourAddress',
        timeout: 60
      })
    });

    Replay protection: When using signatures, signature_deadline must be strictly greater than the last accepted deadline for the maker (deadline monotonicity). Deadlines are bounded to 30 seconds in the future.

    Benefits: Prevents stale orders from sitting on the book when trading bots crash or lose connectivity.

  • New EIP-712 signature type: HeartbeatType

    8th signature type added for signing heartbeat pings:

    const HeartbeatType = [
      { name: 'deadline', type: 'uint256' },
      { name: 'maker', type: 'address' },
      { name: 'timeout', type: 'uint256' },
    ];
  • WebSocket server message envelope fields: message_id and seq_id

    All server messages now include two new fields for reliability:

    • message_id (string): UUID v4 unique to each message — use for deduplication
    • seq_id (number): Monotonically increasing per connection, starting at 1 — use for gap detection
    {
      "kind": "event",
      "type": "trade",
      "timestamp_ms": 1677721600000,
      "message_id": "550e8400-e29b-41d4-a716-446655440000",
      "seq_id": 42,
      "data": { ... }
    }

    Benefits: Enables reliable message processing — detect missed messages via seq_id gaps and avoid processing duplicates via message_id.

  • New WebSocket message type: resend for message recovery

    When a gap in seq_id is detected, request the server to re-deliver missed messages:

    {
      "type": "resend",
      "begin_seq_id": 10,
      "end_seq_id": 15
    }
    • Maximum range: 100 messages per request
    • Messages are cached per connection with limited capacity (LRU eviction) and TTL
    • Resent messages retain their original seq_id and message_id
    • Rate limited: 5 requests per 10 seconds

    Benefits: Enables reliable, lossless event processing without requiring full resubscription after brief network hiccups.

  • New error codes for enhanced validation feedback

    Three new error codes added to StandardError:

    • SIGNATURE_DEADLINE_STALE — Signature deadline is not strictly greater than the last accepted deadline (replay protection)
    • ONE_CT_USER_MISMATCH — One-click trading session was created by a different address than the maker/taker in the request
    • VALUE_INVALID — A request field has an invalid value (e.g., heartbeat timeout out of range)

Changed

  • Breaking Change: Market and combo trade responses simplified

    The unprocessedOrders and rejectedOrders fields have been removed from POST /market and POST /combo responses.

    Before:

    {
      "fillResult": { ... },
      "filledOrders": [...],
      "rejectedOrders": [...],
      "unprocessedOrders": [...]
    }

    After:

    {
      "fillResult": { ... },
      "filledOrders": [...]
    }

    Migration: Remove references to rejectedOrders and unprocessedOrders from response handling code. All successfully processed orders appear in filledOrders.

    Benefits: Cleaner response structure — these fields were vestigial and always empty in practice.

  • PATCH /limit now triggers IOC matching for non-post-only orders crossing the market

    When editing a non-post-only order to a price that crosses the market (e.g., buy price above best ask), the order triggers immediate matching against resting orders (IOC-style). The order's filled_amount resets to 0 and the new size is used for matching.

    Before: Edited orders that crossed the market were rejected.

    After: Non-post-only edited orders that cross the market are immediately matched. Post-only orders crossing the market are still rejected with post only violation.

    Benefits: More flexible order editing for aggressive traders — modify orders without needing to cancel and re-submit.

  • Price and size increment validation now applies to liquidation orders

    Liquidation orders are no longer exempt from price and size increment constraints. All orders (including liquidation: true) must comply with the same increment rules returned by GET /api/v1/exchange_info.

    Benefits: Consistent validation across all order types ensures orderbook integrity.

  • Account history endpoint sorting enhanced

    The sort parameter has been replaced with two parameters:

    • sortKey: Field to sort by — timestamp (default) or realized_pnl
    • sortOrder: Direction — desc (default) or asc

    Response now includes total_count and total_pages when available.

    Before:

    GET /account_history?address=0x...&sort=desc

    After:

    GET /account_history?address=0x...&sortKey=timestamp&sortOrder=desc
    GET /account_history?address=0x...&sortKey=realized_pnl&sortOrder=asc

    Migration: Replace sort parameter with sortKey + sortOrder.

    Benefits: Sort by realized P&L for performance analysis; total counts enable proper pagination UI.

  • POST /calculate_user_risk response: future_settlement_projections now required

    The future_settlement_projections field is now always present in the response (previously optional).

  • WebSocket rate limit corrections

    Message TypeRate LimitWindow (corrected)
    subscribe10 requests1 second (was documented as 10 seconds)
    unsubscribe10 requests1 second (was documented as 10 seconds)
    unsubscribe_all1 request1 second (was 10 req/10s)

Documentation

  • Added POST /heartbeat endpoint to OpenAPI specification
  • Added HeartbeatType as 8th EIP-712 signature type to signatures guide
  • Added resend message type with full request/response examples to WebSocket documentation
  • Added server message envelope fields (message_id, seq_id) section to WebSocket documentation
  • Added gap detection example code to WebSocket documentation
  • Updated rate limit table with resend entry and corrected windows
  • Clarified WebSocket heartbeat note: "no WebSocket-level ping/pong" vs REST heartbeat endpoint

[1.15.0] - 2026-01-31

Added

  • New WebSocket channel: position for real-time position updates

    Subscribe to receive position updates every second for your margin accounts with enriched market data including mark prices and Greeks.

    Subscription example:

    {
      "type": "subscribe",
      "subscriptions": [
        {
          "channel": "position",
          "query": {
            "account": "0x1234567890abcdef1234567890abcdef12345678",
            "market": "ETH"
          }
        }
      ]
    }

    Query parameters:

    • account (required): Smart account address
    • market (optional): Base token filter (BTC, ETH, ARB)

    Event payload:

    {
      "kind": "event",
      "type": "position",
      "timestamp_ms": 1705420800000,
      "data": {
        "margin_account": "550e8400-e29b-41d4-a716-446655440000",
        "market": "ETH",
        "positions": [
          {
            "instrument_name": "ETH_USDC-PERPETUAL",
            "size": 10.5,
            "average_price": 2450.00,
            "instrument_type": "perp",
            "mark_price": 2475.00,
            "current_funding_rate": 0.0001,
            "position_greeks": { "delta": 10.5 }
          }
        ]
      }
    }

    Benefits: Dedicated position channel provides 1-second updates with enriched market data, separate from account state events.

  • Account History V2 endpoint with cursor-based pagination

    Enhanced account history endpoint at /v2/account_history with standardized event structures, advanced filtering, and efficient cursor-based pagination.

    New features:

    • Cursor-based pagination via cursor and next_cursor fields
    • Sort order control (sort: asc/desc)
    • Event type filtering (event_types: trade, transfer, settlement, funding)
    • Action filtering (actions: buy, sell, deposit, withdrawal)
    • Transfer type filtering (transfer_type: internal, external)
    • Market/instrument filtering (markets: BTC, ETH, ARB)

    Request example:

    const params = new URLSearchParams({
      address: '0x1234...',
      event_types: 'trade,transfer',
      sort: 'desc',
      limit: '50',
    });
    const response = await fetch(`/v2/account_history?${params}`);
    const data = await response.json();
    
    // Paginate with cursor
    if (data.has_more) {
      const nextPage = await fetch(data.next_cursor_url);
    }

    Response structure:

    {
      "address": "0x1234...",
      "limit": 50,
      "returned": 50,
      "has_more": true,
      "next_cursor": "eyJ0aW1lc3RhbXAi...",
      "next_cursor_url": "https://api.kyan.sh/v2/account_history?...",
      "events": [
        {
          "id": "evt_001",
          "timestamp": 1705420800,
          "event_type": "trade",
          "margin_account_id": "...",
          "data": { /* event-specific data */ }
        }
      ]
    }

    Benefits: Efficient retrieval of large datasets, consistent event structures, and flexible filtering reduce client-side processing.

Changed

  • Breaking Change: account_state event no longer includes positions array

    The account_state WebSocket event payload has been streamlined to remove the positions field.

    Before:

    {
      "type": "account_state",
      "data": {
        "margin_account": 12345,
        "pair": "BTC_USDC",
        "im": 5000.0,
        "mm": 2500.0,
        "equity": 15000.0,
        "portfolio_greeks": { "delta": 0.45, "gamma": 0.0012, ... },
        "positions": [{ "instrument_name": "...", "size": 1.5, ... }]
      }
    }

    After:

    {
      "type": "account_state",
      "data": {
        "margin_account": 12345,
        "pair": "BTC_USDC",
        "im": 5000.0,
        "mm": 2500.0,
        "equity": 15000.0,
        "portfolio_greeks": { "delta": 0.45, "gamma": 0.0012, ... },
        "liquidation": false
      }
    }

    Migration: Subscribe to the new position channel for real-time position data with enriched market information.

    Benefits: Reduced payload size and bandwidth; position data now available via dedicated channel with 1-second updates.

  • Breaking Change: Order events now use batched array format

    WebSocket order events (post_order, cancel_order, update_order, edit_order) now deliver orders in arrays, allowing multiple orders per message.

    Before:

    {
      "type": "post_order",
      "data": {
        "order_id": "abc123",
        "instrument_name": "BTC_USDC-PERPETUAL",
        "amount": 1000,
        "price": 98500
      }
    }

    After:

    {
      "type": "post_order",
      "data": [
        {
          "order_id": "abc123",
          "instrument_name": "BTC_USDC-PERPETUAL",
          "amount": 1000,
          "price": 98500
        }
      ]
    }

    Migration:

    // Before
    if (event.type === 'post_order') {
      processOrder(event.data);
    }
    
    // After
    if (event.type === 'post_order') {
      for (const order of event.data) {
        processOrder(order);
      }
    }

    Benefits: Efficient batch delivery reduces message overhead for high-frequency order updates. Single-order events are delivered as single-element arrays for consistency.

  • New edit_order WebSocket event type

    When orders are edited via PATCH /limit, an edit_order event is now emitted on orderbook channels (orderbook_options, orderbook_perps, orderbook_maker).

    Event payload:

    {
      "type": "edit_order",
      "data": [
        {
          "order_id": "abc123",
          "instrument_name": "BTC_USDC-PERPETUAL",
          "price": 99000,
          "amount": 1500,
          "order_state": "open",
          "filled_amount": 0
        }
      ]
    }

    Note: The edited order retains its original order_id. Use this event to update local orderbook caches when order prices or sizes change.

[1.14.0] - 2026-01-07

Added

  • Price increment validation for order prices

    Order prices must now be divisible by the minimum price increment for the base asset. This ensures consistent pricing across all trading pairs and improves orderbook efficiency.

    Price increments by asset:

    AssetMinimum Increment
    BTC$1.00
    ETH$0.10
    ARB$0.01

    Affected endpoints:

    • POST /limit - validates price field
    • PATCH /limit - validates price field
    • POST /market - validates limit_price field
    • POST /combo - validates limit_perp_price field (perp leg only; option legs are exempt)

    New error code: MIN_INCREMENT_PRICE_VIOLATION

    Orders with non-compliant prices will be rejected with HTTP 400:

    {
      "error": "MIN_INCREMENT_PRICE_VIOLATION",
      "message": "Min price increment requirement violated for ETH_USDC-PERPETUAL. Price: 2500.15, Must be divisible by: 0.1"
    }

    Migration:

    // Before (may fail validation)
    const order = {
      instrument_name: 'ETH_USDC-PERPETUAL',
      price: 2500.15,  // Not divisible by 0.1
      amount: 1000
    };
    
    // After (compliant)
    const order = {
      instrument_name: 'ETH_USDC-PERPETUAL',
      price: 2500.10,  // Divisible by 0.1
      amount: 1000
    };

    Benefits: Consistent pricing grid improves orderbook depth aggregation and reduces price fragmentation.

  • Size increment constraints now documented

    Order sizes must be divisible by the size increment for the asset. This validation was already enforced but is now explicitly documented with programmatic access via /api/v1/exchange_info.

    Size increments:

    InstrumentAssetSize Increment
    OptionsBTC0.01 contracts
    OptionsETH0.1 contracts
    OptionsARB100 contracts
    PerpetualsAll$1 USD

    Error code: MIN_INCREMENT_SIZE_VIOLATION

    {
      "error": "MIN_INCREMENT_SIZE_VIOLATION",
      "message": "Min size increment requirement violated for BTC_USDC-PERPETUAL. Size: 1.5, Must be divisible by: 1"
    }
  • GET /api/v1/exchange_info endpoint now documented

    This public endpoint (no authentication required) returns static exchange configuration including:

    • Trading pairs and their status
    • Order constraints (price increments, minimum sizes, max slippage)
    • Instrument specifications and naming formats
    • Fee structures
    • Rate limits
    • WebSocket channel definitions

    Usage:

    const response = await fetch('https://sandbox.kyan.sh/api/v1/exchange_info');
    const config = await response.json();
    
    // Access price increments
    const btcIncrement = config.orderConstraints.priceIncrements.constraints.BTC; // 1
    const ethIncrement = config.orderConstraints.priceIncrements.constraints.ETH; // 0.1
    const arbIncrement = config.orderConstraints.priceIncrements.constraints.ARB; // 0.01

    Benefits: Programmatic access to exchange configuration enables dynamic validation and reduces hardcoded values in client applications.

[1.13.0] - 2025-12-16

Added

  • New endpoint: PATCH /limit for editing existing limit orders

    • Purpose: Modify price and size of existing orders without canceling and resubmitting
    • Benefits: Atomic updates preserve queue position for unchanged price, reduced latency for order modifications
    • Authentication: Requires EIP-712 signature (one-click sessions not supported)

    Request example (options):

    const editRequest = {
      order_id: "abc123def456...",
      price: 2600.0,           // New price
      contracts: 2.0,          // New size for options
      signature: "0x...",
      signature_deadline: 1735689600
    };
    
    const response = await fetch('/limit', {
      method: 'PATCH',
      headers: {
        'x-apikey': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(editRequest)
    });

    Request example (perpetuals):

    const editRequest = {
      order_id: "abc123def456...",
      price: 98500.0,          // New price
      amount: 15000,           // New size for perpetuals
      signature: "0x...",
      signature_deadline: 1735689600
    };

    Constraints:

    • Order must exist and belong to the signing maker
    • Cannot edit orders during active MMP freeze
    • Must edit within the same market as original order
    • Size must meet minimum increment requirements

    Response: Returns the updated StoredLimitOrder with new values and edit_order event emitted on WebSocket

Changed

  • Correction: WebSocket trade events do not include order metadata

    Trade events contain execution data only. The following fields are not present on trade events:

    • user_id - Use order events for user tracking
    • order_size - Use order events for original order size
    • filled_amount - Use order events for cumulative fill tracking

    Trade event structure (actual):

    {
      "kind": "event",
      "type": "trade",
      "timestamp_ms": 1734355200000,
      "data": {
        "margin_account_id": "550e8400-e29b-41d4-a716-446655440000",
        "smart_account_address": "0x1234567890abcdef1234567890abcdef12345678",
        "trade_id": "1234567890abcdef1234567890abcdef",
        "order_id": "abcdef1234567890abcdef1234567890",
        "size": 1.5,
        "timestamp": 1734355200000,
        "direction": "buy",
        "index_price": 45000.5,
        "instrument_name": "BTC_USDC-31DEC25-100000-C",
        "average_price": 2500.0,
        "limit_price": 2500.0,
        "liquidation": false,
        "iv": 0.65,
        "taker_fee": 0.50,
        "maker_fee": 0.25,
        "systemic_risk_fee": 0.10,
        "liquidation_fee": 0.0,
        "realised_funding": 0.0
      }
    }

    Migration: For order lifecycle metadata including user_id, total order size, and cumulative filled_amount, subscribe to order events (post_order, update_order, cancel_order) via orderbook channels.

  • WebSocket order events now include user_id field

    Order events (post_order, cancel_order, update_order, edit_order) include the user_id field when provided during order submission:

    {
      "kind": "event",
      "type": "post_order",
      "data": {
        "instrument_name": "BTC_USDC-31DEC25-100000-C",
        "order_id": "abc123...",
        "user_id": "trader_001",
        "maker": "0x1234...",
        ...
      }
    }

    Benefits: Track orders across their full lifecycle using your custom identifiers

Fixed

  • POST /limit error messages now match backend validation exactly:
    • Added: "order size not divisible by minimum increment"
    • Fixed: "multiple markets provided" message formatting
  • PATCH /limit error messages aligned with ORDERBOOK_API_ERROR_MESSAGES constants

[1.12.0] - 2025-11-27

Added

  • New update_order WebSocket event for tracking order fill progress

    • Purpose: Receive real-time notifications when orders are partially or completely filled, enabling precise order state tracking without polling
    • Benefits: Instant visibility into order execution, reduced API calls, accurate fill tracking for IOC orders
    • New capabilities: Track partial fills, monitor order state transitions, synchronize local order state with exchange

    When emitted:

    • After a partial fill (order remains open with updated filled_amount)
    • After a complete fill (order state changes to filled)
    • When IOC (Immediate-or-Cancel) orders are partially filled and reinstated

    Event data structure:

    {
      "kind": "event",
      "type": "update_order",
      "timestamp_ms": 1732713600000,
      "data": {
        "instrument_name": "BTC_USDC-31DEC25-100000-C",
        "type": "good_til_cancelled",
        "contracts": 10.0,
        "direction": "buy",
        "price": 2500.0,
        "post_only": false,
        "mmp": false,
        "liquidation": false,
        "maker": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
        "taker": "0x0000000000000000000000000000000000000000",
        "order_id": "abc123def456...",
        "user_id": "user_123",
        "order_state": "open",
        "filled_amount": 3.5,
        "creation_timestamp": 1732713500000,
        "chain_id": 421614,
        "deadline": 1732800000,
        "signature": "0x..."
      },
      "subscription": {
        "channel": "orderbook",
        "query": {
          "instrument_name": "BTC_USDC-31DEC25-100000-C"
        }
      }
    }

    Key features:

    • Complete order state: Event contains the full order object with all fields, not just changes
    • No delta processing: Use the filled_amount and order_state directly without calculating deltas
    • Book building compatible: The complete order state allows maintaining accurate orderbook representation

    Use cases:

    • Trading applications tracking order execution progress
    • Market makers monitoring fill rates
    • Order management systems maintaining accurate order state
    • Analytics systems tracking order lifecycle events

Changed

  • Rate limiting now enforced per API key owner instead of per session

    Before:

    // Old behavior: Each WebSocket connection had independent rate limits
    const ws1 = new WebSocket('wss://api.premia.io');  // Separate rate limit
    const ws2 = new WebSocket('wss://api.premia.io');  // Separate rate limit
    // Each connection could send full quota of requests

    After:

    // New behavior: All connections using same API key share rate limits
    const ws1 = new WebSocket('wss://api.premia.io');  // Shared rate limit pool
    const ws2 = new WebSocket('wss://api.premia.io');  // Shared rate limit pool
    // Total requests across all connections must stay within limits

    Benefits:

    • Fair resource allocation: Prevents rate limit circumvention via multiple connections
    • Better system stability: More predictable load patterns
    • Account-level control: Rate limits align with API key ownership

    Rate limit details:

    Message TypeRate LimitWindowScope
    auth1 request1 secondPer client IP address
    get_instruments1 request1 secondPer API key owner
    get_ob_state_by_instruments5 requests20 secondsPer API key owner

    Rate limit exceeded response:

    {
      "kind": "response",
      "id": "req-123",
      "type": "rate_limit_exceeded",
      "data": {
        "message": "Rate limit exceeded. Please try again later.",
        "retry_after_seconds": 15
      }
    }

    Migration:

    1. Review applications with multiple WebSocket connections using the same API key
    2. Implement request coordination across connections to share rate limit quota
    3. Consider consolidating multiple connections if possible
    4. Handle rate_limit_exceeded responses with exponential backoff using retry_after_seconds
    5. Monitor rate limit errors to optimize request patterns
  • Breaking Change: Orderbook bids/asks arrays now include order_id as third element

    Before:

    // Old format: [price, amount]
    const orderbook = await fetch('/order_book?instrument_name=BTC_USDC-PERPETUAL');
    const data = await orderbook.json();
    
    // Process bids/asks
    data.bids.forEach(([price, amount]) => {
      console.log(`Bid: ${amount} @ ${price}`);
      // No way to identify specific orders
    });

    After:

    // New format: [price, amount, order_id]
    const orderbook = await fetch('/order_book?instrument_name=BTC_USDC-PERPETUAL');
    const data = await orderbook.json();
    
    // Process bids/asks with order tracking
    data.bids.forEach(([price, amount, orderId]) => {
      console.log(`Bid: ${amount} @ ${price} (Order: ${orderId})`);
      // Can now track specific orders and match with update_order events
    });

    Example REST response:

    {
      "timestamp": 1732713600,
      "instrument_name": "BTC_USDC-PERPETUAL",
      "index_price": 98500.00,
      "bids": [
        [98450.00, 2.5, "order_abc123"],
        [98400.00, 1.8, "order_def456"],
        [98350.00, 3.2, "order_ghi789"]
      ],
      "asks": [
        [98550.00, 1.9, "order_jkl012"],
        [98600.00, 2.1, "order_mno345"],
        [98650.00, 1.5, "order_pqr678"]
      ],
      "best_bid_price": 98450.00,
      "best_ask_price": 98550.00
    }

    Example WebSocket snapshot:

    {
      "kind": "snapshot",
      "type": "orderbook",
      "timestamp_ms": 1732713600000,
      "data": {
        "bids": [
          [98450.00, 2.5, "order_abc123"],
          [98400.00, 1.8, "order_def456"]
        ],
        "asks": [
          [98550.00, 1.9, "order_jkl012"],
          [98600.00, 2.1, "order_mno345"]
        ]
      },
      "subscription": {
        "channel": "orderbook",
        "query": {
          "instrument_name": "BTC_USDC-PERPETUAL"
        }
      }
    }

    Benefits:

    • Order tracking: Match orderbook levels with specific limit orders
    • Event correlation: Connect update_order events with orderbook state
    • Enhanced analytics: Track order lifecycle from placement to fill
    • Better debugging: Identify specific orders causing issues

    Migration:

    1. Update array destructuring to include third element: [price, amount, orderId]
    2. Update TypeScript types: Array<[number, number, string]>
    3. Utilize order_id to correlate with update_order and post_order events
    4. Update any hardcoded array length checks from 2 to 3
    5. Test orderbook rendering with new three-element format
  • Enhanced skip_snapshot query parameter behavior for orderbook subscriptions

    Before:

    // Old behavior: skip_snapshot only prevented initial snapshot
    {
      "type": "subscribe",
      "subscriptions": [{
        "channel": "orderbook",
        "query": {
          "instrument_name": "BTC_USDC-PERPETUAL",
          "skip_snapshot": true  // Skip initial snapshot only
        }
      }]
    }
    // Would still receive snapshot if re-subscribing to same instrument

    After:

    // New behavior: skip_snapshot prevents duplicate snapshots
    {
      "type": "subscribe",
      "subscriptions": [{
        "channel": "orderbook",
        "query": {
          "instrument_name": "BTC_USDC-PERPETUAL",
          "skip_snapshot": true  // Skip snapshot for duplicate subscriptions
        }
      }]
    }
    // Server detects duplicate subscriptions and skips redundant snapshots
    // Still receive post_order, cancel_order, and update_order events

    Benefits:

    • Reduced bandwidth: Eliminates redundant snapshot data for duplicate subscriptions
    • Faster reconnection: Re-subscribe without downloading full orderbook again
    • Efficient multi-instrument: Subscribe to multiple instruments without snapshot overhead

    Use cases:

    1. Connection recovery: Re-subscribe after brief disconnect without re-downloading full orderbook
    2. Dynamic subscriptions: Add/remove instruments while maintaining existing subscriptions
    3. Bandwidth optimization: Minimize data transfer for high-frequency subscription updates

    Migration:

    • No changes required for existing implementations
    • Consider using skip_snapshot: true when re-subscribing to reduce bandwidth
    • Ensure application can handle orderbook updates without initial snapshot state

Documentation

  • Complete WebSocket API documentation updates

    • Added update_order event to Event Types table
    • Added comprehensive update_order event documentation section with examples
    • Updated rate limiting section with new per-owner enforcement and complete rate limit table
    • Updated rate limit response format to match current JSON structure
    • Updated orderbook array format examples throughout documentation
    • Added "Orderbook Array Format" explanation section
    • Enhanced skip_snapshot parameter description with duplicate detection behavior
  • Complete REST API documentation updates

    • Updated /order_book endpoint OpenAPI schema for 3-element bids/asks arrays
    • Modified Orderbook.yaml schema to include order_id as third element
    • Updated array type definitions to [number, number, string] format
    • Added descriptive comments explaining the three-element tuple structure

[1.11.0] - 2025-10-21

Added

  • New instruments WebSocket channel for real-time instrument list updates

    • Purpose: Receive immediate notifications when trading instruments are added or expire, keeping your application synchronized with available markets
    • Benefits: Eliminates need for polling /instruments endpoint, instant awareness of new trading opportunities, automatic cleanup of expired instruments
    • New capabilities: Subscribe to specific markets (BTC, ETH, ARB) or all markets, receive initial snapshot on subscription, get update timestamp with each event

    Example subscription (single market):

    {
      "type": "subscribe",
      "subscriptions": [
        {
          "channel": "instruments",
          "query": {
            "market": "BTC"
          }
        }
      ]
    }

    Example subscription (all markets):

    {
      "type": "subscribe",
      "subscriptions": [
        {
          "channel": "instruments",
          "query": {}
        }
      ]
    }

    Event data received:

    {
      "kind": "event",
      "type": "instruments",
      "timestamp_ms": 1677721600000,
      "data": {
        "updated_at": 1677721600000,
        "instruments": [
          "BTC_USDC-PERPETUAL",
          "BTC_USDC-31OCT25-130000-C",
          "BTC_USDC-31OCT25-130000-P",
          "BTC_USDC-31OCT25-135000-C",
          "BTC_USDC-31OCT25-135000-P"
        ]
      },
      "subscription": {
        "channel": "instruments",
        "query": {
          "market": "BTC"
        }
      }
    }

    Data fields:

    • updated_at: Unix timestamp in milliseconds
    • instruments: Array of instrument names currently available for trading

    Query parameters:

    • market (optional): Base token (BTC, ETH, or ARB). If omitted, subscribes to all markets

    Use cases:

    • Trading applications dynamically updating available instruments
    • Automated trading systems detecting new option expirations
    • Market monitoring tools tracking instrument lifecycle
    • Risk management systems responding to expired instruments

Changed

  • Breaking Change: Endpoint GET /instruments now returns object with timestamp instead of array

    Before:

    // Old response format
    const response = await fetch('/instruments?market=BTC');
    const instruments: string[] = await response.json();
    // Returns: ["BTC_USDC-PERPETUAL", "BTC_USDC-31OCT25-130000-C", ...]

    After:

    // New response format
    const response = await fetch('/instruments?market=BTC');
    const data: {
      updated_at: number;  // Unix timestamp in milliseconds
      instruments: string[];
    } = await response.json();
    // Returns: {
    //   updated_at: 1677721600000,
    //   instruments: ["BTC_USDC-PERPETUAL", "BTC_USDC-31OCT25-130000-C", ...]
    // }
    
    // Access instruments array
    const instruments = data.instruments;
    // Use timestamp for caching/change detection
    const lastUpdate = new Date(data.updated_at);  // Convert to Date object

    Benefits:

    • Cache optimization: Use updated_at timestamp (milliseconds) to implement efficient caching strategies
    • Change detection: Compare timestamps to determine if instrument list has changed
    • Synchronization: Coordinate REST API data with WebSocket instruments channel updates using the same timestamp
    • Better performance: Avoid unnecessary processing when instrument list hasn't changed

    Migration:

    1. Update response type to expect object instead of array
    2. Access instruments via data.instruments property
    3. Optionally implement caching using data.updated_at timestamp
    4. Consider migrating to WebSocket instruments channel for real-time updates
  • Breaking Change: Endpoint POST /limit response format simplified - matched field removed

    Before:

    // Old response format
    const response = await fetch('/limit', {
      method: 'POST',
      body: JSON.stringify([/* orders */])
    });
    const result: {
      posted: Array<Order>;
      matched: Array<Order>;  // Separate array for matched orders
      rejected: Array<Order>;
    } = await response.json();

    After:

    // New response format
    const response = await fetch('/limit', {
      method: 'POST',
      body: JSON.stringify([/* orders */])
    });
    const result: {
      posted: Array<Order>;   // Now includes both posted AND matched orders
      rejected: Array<Order>;
    } = await response.json();
    // Note: matched field no longer exists - matched orders are merged into 'posted' array

    Benefits:

    • Simplified response structure: Easier to process all successful orders in one array
    • Reduced complexity: No need to merge posted and matched arrays client-side
    • Clearer semantics: All successfully processed orders (whether posted to orderbook or immediately matched) are in posted

    Migration:

    1. Update response type to remove matched field
    2. Process all successful orders from the posted array (includes both posted and matched orders)
    3. If you need to distinguish matched orders, check the order_state field (will be filled or open)
    4. Remove any client-side code that separately processed the matched array

Documentation

  • Complete REST API documentation updates

    • Updated /instruments endpoint OpenAPI schema with new InstrumentsData response type
    • Added detailed field descriptions for updated_at and instruments properties
    • Updated endpoint description to highlight new timestamp functionality
  • Complete WebSocket API documentation updates

    • Added detailed channel description for instruments channel
    • Added event data structures with field descriptions
    • Added subscription examples for both single market and all markets scenarios
    • Updated Available Channels table with new instruments entry
    • Updated Event Types table with instruments event
    • Added comprehensive use cases and implementation notes

[1.10.0] - 2025-10-14

Added

  • New mmp WebSocket channel for Market Maker Protection events

    • Purpose: Monitor when MMP (Market Maker Protection) is triggered, temporarily freezing an account's trading when risk thresholds are exceeded
    • Benefits: Real-time notifications of risk threshold violations, proactive risk management, automated trading pause awareness
    • New capabilities: Track freeze status, monitor freeze duration, filter by account or trading pair

    Example subscription:

    {
      "channel": "mmp",
      "query": {
        "smart_account_address": "0x1234567890abcdef1234567890abcdef12345678",
        "pair": "BTC_USDC"  // optional
      }
    }

    Event data received:

    {
      "kind": "event",
      "type": "mmp_triggered",
      "timestamp_ms": 1677721600000,
      "data": {
        "smart_account_address": "0xabCDEF1234567890ABcDEF1234567890aBCDeF12",
        "pair_symbol": "ETH_USDC",
        "frozen_until": 1677721660000,
        "frozen_duration_seconds": 60
      }
    }

    Query parameters:

    • smart_account_address (optional): Filter events for a specific smart account
    • pair (optional): Filter events for a specific trading pair (BTC_USDC, ETH_USDC, or ARB_USDC)

    Use cases:

    • Market makers monitoring their own MMP status
    • Risk management systems tracking protection triggers
    • Automated trading bots pausing operations during freeze periods
  • New bankruptcy WebSocket channel for account bankruptcy events

    • Purpose: Monitor when account equity becomes negative after liquidation attempts, representing losses absorbed by the insurance fund
    • Benefits: Real-time bankruptcy notifications, insurance fund impact tracking, systemic risk monitoring
    • New capabilities: Track bankruptcy events by market, monitor insurance fund utilization

    Example subscription:

    {
      "channel": "bankruptcy",
      "query": {
        "market": "ETH_USDC"  // optional
      }
    }

    Event data received:

    {
      "kind": "event",
      "type": "bankruptcy",
      "timestamp_ms": 1677721600000,
      "data": {
        "marginAccountId": "550e8400-e29b-41d4-a716-446655440000",
        "market": "ETH_USDC"
      }
    }

    Query parameters:

    • market (optional): Filter events by trading pair (BTC_USDC, ETH_USDC, ARB_USDC, or USDC_USDC)

    Use cases:

    • Risk managers monitoring systemic risk indicators
    • Insurance fund tracking and analysis
    • Market health monitoring and alerting

Documentation

  • Complete WebSocket API documentation updates
    • Added detailed channel descriptions for mmp and bankruptcy channels
    • Added event data structures with field descriptions
    • Added subscription examples with multiple query parameter combinations
    • Updated Available Channels table with new entries
    • Updated Event Types table with mmp_triggered and bankruptcy events

[1.9.0] - 2025-09-29

Added

  • Future settlement projections in risk calculations (POST /calculate_user_risk)

    • New field: future_settlement_projections object containing risk metrics for future expiration dates
    • Benefits: Evaluate how portfolio risk evolves as options approach expiration, better risk planning for future scenarios
    • Capabilities: Each settlement date includes projected margin requirements (im, mm), risk components (matrix_risk, delta_risk, roll_risk), and complete Greeks

    Example response:

    {
      "status": "success",
      "data": {
        "pair": "ETH_USDC",
        "im": 1200.00,
        "mm": 1000.00,
        "greeks": { /* current greeks */ },
        "future_settlement_projections": {
          "2025-10-31": {
            "im": 1440.00,
            "mm": 1200.00,
            "matrix_risk": 960.00,
            "delta_risk": 180.00,
            "roll_risk": 60.00,
            "greeks": {
              "delta": 1.8,
              "gamma": 0.008,
              "vega": 90.0,
              "theta": -12.0,
              "rho": 4.5
            }
          }
        }
      }
    }
  • New orderbook_maker WebSocket channel for market makers

    • Purpose: Dedicated channel for market makers to monitor their own orders efficiently
    • Benefits: Initial snapshot of all maker's active orders, real-time updates for only that maker's activity, reduced bandwidth and processing overhead
    • Migration: Replaces the maker filter previously available in orderbook_options and orderbook_perps channels

    Example subscription:

    {
      "channel": "orderbook_maker",
      "query": {
        "maker": "0x1234567890abcdef1234567890abcdef12345678",
        "pair": "BTC_USDC"  // optional
      }
    }

    Events received:

    • Initial: ob_maker_orders event with all active orders
    • Ongoing: post_order and cancel_order events for that maker only
  • Session revocation enhancements (DELETE /session)

    • New capability: Delete all one-click trading sessions for an API key by omitting the x-one-click header
    • Benefits: Bulk session cleanup, simplified session management for applications with multiple sessions
    • Response: Returns count of revoked sessions

    Example - Revoke all sessions:

    // Without x-one-click header - revokes ALL sessions
    const response = await fetch('/session', {
      method: 'DELETE',
      headers: {
        'x-apikey': 'your-api-key'
        // No x-one-click header
      }
    });
    // Response: { "revokedSessions": 5 }
  • User tracking field in market order responses

    • New field: user_id added to POST market order responses for analytics and tracking
    • Benefits: Better order attribution, enhanced analytics capabilities, improved debugging
    • Location: Returned in response body after successful market order submission

Changed

  • Breaking Change: WebSocket maker filtering moved to dedicated channel

    • Removed: maker parameter no longer available in orderbook_options and orderbook_perps channels
    • Replacement: Use the new orderbook_maker channel for maker-specific filtering

    Before (deprecated):

    {
      "channel": "orderbook_options",
      "query": {
        "pair": "BTC_USDC",
        "maker": "0x1234..."  // No longer supported
      }
    }

    After (required):

    {
      "channel": "orderbook_maker",
      "query": {
        "maker": "0x1234...",  // Now in dedicated channel
        "pair": "BTC_USDC"
      }
    }

    Benefits: Better performance isolation, cleaner API design, optimized for market maker use cases

Updated

  • RFQ response requirements clarified
    • Clarification: RFQ responses must contain either all option orders OR exactly one perpetual order
    • Validation: Cannot mix options and perpetuals in the same RFQ response
    • Documentation: Enhanced examples and constraints in RFQ documentation

[1.8.1] - 2025-09-02

Added Documentation Error (Corrected in 1.13.0)

  • WebSocket trade events now include order size and fill information

    ⚠️ Correction: This entry was documented in error. Trade events do NOT include order_size or filled_amount fields. These fields are available on order events (post_order, update_order, cancel_order), not trade events. See version 1.13.0 for the correct trade event structure.

    For order lifecycle tracking, subscribe to orderbook channels and use the update_order event (added in 1.12.0) which provides complete order state including filled_amount.

[1.8.0] - 2025-08-30

Added

  • WebSocket orderbook subscription filtering by maker address

    • New capability: Filter orderbook events (post_order, cancel_order, trade) by specific market maker address
    • Benefits: Reduces bandwidth usage by receiving only relevant orderbook updates, enables tracking specific market makers
    • Channels affected: orderbook_options, orderbook_perps

    Example:

    // Subscribe to orderbook events from a specific maker only
    const subscription = {
      channel: 'orderbook_options',
      query: {
        pair: 'BTC_USDC',
        maker: '0x1234567890abcdef1234567890abcdef12345678'
      }
    };
  • WebSocket orderbook snapshot skipping option

    • New capability: Skip initial orderbook snapshot when subscribing to orderbook channels
    • Benefits: Faster subscription initialization, reduced initial data transfer for clients that only need incremental updates
    • Use case: Ideal for applications that maintain their own orderbook state or only need real-time updates

    Example:

    // Subscribe without receiving initial orderbook state
    const subscription = {
      channel: 'orderbook_perps',
      query: {
        instrument_name: 'BTC_USDC-PERPETUAL',
        options: {
          skip_snapshot: true  // Only receive incremental updates
        }
      }
    };

Changed

  • WebSocket orderbook subscriptions now support optional pair parameter

    • Breaking Change: The pair parameter is now optional for orderbook_options and orderbook_perps channels
    • Benefits: Subscribe to all instruments across all markets in a single subscription, simplified market-wide monitoring

    Before:

    // Previously required to specify a pair
    const subscription = {
      channel: 'orderbook_options',
      query: {
        pair: 'BTC_USDC',  // Was required
        maturity: '29AUG25'
      }
    };

    After:

    // Now can subscribe to all markets at once
    const subscription = {
      channel: 'orderbook_options',
      query: {
        // pair is now optional - omit to receive all markets
        maturity: '29AUG25'  // Can filter by maturity across all pairs
      }
    };
    
    // Or subscribe to absolutely everything
    const subscribeToAll = {
      channel: 'orderbook_perps',
      query: {}  // Receives all perpetual orderbook events
    };

    Migration Guide:

    • Existing subscriptions with pair specified will continue to work unchanged
    • To subscribe to all markets, simply omit the pair parameter
    • Consider using the new maker filter or skip_snapshot option to manage data volume when subscribing broadly

[1.7.0] - 2025-08-27

🔄 Changed - RFQ Response Event Improvements

Breaking Change: WebSocket rfq_response Event Split into Two Distinct Events

The single rfq_response WebSocket event has been split into two semantically distinct events for improved clarity and event handling:

  • rfq_post_response: Emitted when a market maker posts a new response to an RFQ request with pricing
  • rfq_cancel_response: Emitted when a market maker cancels a previously posted RFQ response

Before:

// Single event type for both posting and cancelling
ws.on('message', (msg) => {
  if (msg.type === 'rfq_response') {
    // Had to check fills array to determine if it was a post or cancel
    if (msg.data.fills && msg.data.fills.length > 0) {
      // Handle new response
    } else {
      // Handle cancellation
    }
  }
});

After:

// Distinct event types for clearer handling
ws.on('message', (msg) => {
  if (msg.type === 'rfq_post_response') {
    // Handle new RFQ response with pricing
    const fills = msg.data.fills; // Always contains pricing data
    processPricing(fills);
  } else if (msg.type === 'rfq_cancel_response') {
    // Handle RFQ response cancellation
    const responseId = msg.data.response_id;
    removePricing(responseId);
  }
});

Benefits:

  • Clearer semantics: Event type immediately indicates the action without checking payload
  • Improved type safety: TypeScript users get better type inference for each event
  • Simplified event handling: No need to check fills array to determine event intent
  • Better observability: Easier to track and monitor different RFQ response actions

Migration Guide:

  1. Update WebSocket message handlers to check for rfq_post_response and rfq_cancel_response instead of rfq_response
  2. Remove any logic that checks the fills array to determine if it's a cancellation
  3. Handle each event type with its specific business logic

Note: RFQ cancellations are now properly emitted only on the rfq channel and no longer appear on orderbook channels.

[1.6.1] - 2025-08-27

📝 Fixed - WebSocket Documentation Corrections

Corrected WebSocket Message kind Field Values

  • Fixed documentation to accurately reflect the WebSocket message kind field values used in the actual implementation
  • Previous documentation incorrectly showed uppercase values ("RESPONSE", "EVENT")
  • Corrected values now show lowercase as per the codebase ("response", "event")

Before (Incorrect):

{
  "kind": "RESPONSE", // ❌ Incorrect - uppercase
  "type": "auth",
  "id": "request-123",
  "timestamp_ms": 1677721600000,
  "success": true
}

After (Correct):

{
  "kind": "response", // ✅ Correct - lowercase
  "type": "auth",
  "id": "request-123",
  "timestamp_ms": 1677721600000,
  "success": true
}

Impact:

  • No functional changes to the WebSocket API behavior
  • Documentation only update to match actual implementation

[1.6.0] - 2025-08-20

✨ Added - Account History Endpoint

New Endpoint: GET /account_history for Complete Trading History

  • New public REST endpoint for retrieving comprehensive account trading history and events
  • Consolidated view: Access all trades and account events (deposits, withdrawals, settlements, funding) in a single request
  • Flexible filtering: Query by time range and limit results for efficient data retrieval
  • Complete audit trail: Track all account activity for reconciliation and analysis

Usage Example:

// Retrieve account history with optional filters
const response = await fetch('/account_history?' + new URLSearchParams({
  address: '0x9a2f0C371820e8BE3D64efb3E814808F231880ff',
  start_timestamp: '1704067200',  // Optional: Unix timestamp
  end_timestamp: '1704153600',    // Optional: Unix timestamp
  count: '100'                    // Optional: Max records to return
}));

const data = await response.json();
// Response structure
{
  address: '0x9a2f0C371820e8BE3D64efb3E814808F231880ff',
  history: {
    trades: [
      {
        trade_id: 'a1b2c3d4e5f678901234567890abcdef',
        instrument_name: 'BTC_USDC-29AUG25-106000-C',
        direction: 'buy',
        size: 1.5,
        average_price: 125.50,
        realised_pnl: -5.75,
        // ... full trade details including fees and greeks
      }
    ],
    account_events: [
      {
        action: 'deposit',  // Types: deposit, withdrawal, settlement, funding
        amount: 1000.0,
        symbol: 'USDC',
        timestamp: 1704067500,
        // ... complete event details
      }
    ]
  }
}

[1.5.0] - 2025-08-02

✨ Added - Enhanced Order Tracking

Optional User ID Field for Order Tracking

  • Added user_id field to all order types for enhanced tracking and analytics:
    • REST Endpoints: All limit orders (POST /limit), market orders (POST /market), combo orders (POST /combo), and RFQ orders
    • WebSocket Events: Trade events now include user_id field in data payload
    • Field Type: Optional string with maximum 36 characters
    • Use Cases: Custom order labeling, client-side tracking, analytics categorization, portfolio grouping

Migration Example:

// Before (existing functionality continues to work)
const limitOrder = {
  instrument_name: 'BTC_USDC-29AUG25-106000-C',
  type: 'good_til_cancelled',
  contracts: 1.5,
  direction: 'buy',
  price: 1000.5,
  // ... other required fields
};

// After (optional user_id field available)
const limitOrder = {
  instrument_name: 'BTC_USDC-29AUG25-106000-C',
  type: 'good_til_cancelled',
  contracts: 1.5,
  direction: 'buy',
  price: 1000.5,
  user_id: 'portfolio_a_trade_001', // New optional field
  // ... other required fields
};

Benefits:

  • Custom tracking: Label orders with your own identifiers for easier management
  • Portfolio organization: Group orders by strategy, client, or trading session
  • Analytics enhancement: Track performance metrics across custom categories
  • Backwards compatible: Existing code continues to work without changes

🔧 Changed - RFQ Validation and Rate Limiting

Breaking Change: RFQ Opposite Direction Requirement

  • RFQ responses now require opposite directions between taker requests and maker limit orders
  • Traditional market making: Takers request one direction, makers provide liquidity in the opposite direction
  • Enhanced validation: System validates direction compatibility before accepting RFQ responses

Before:

// Previously allowed (incorrect market making)
const rfqResponse = {
  fills: [
    [
      {
        instrument_name: 'BTC_USDC-29AUG25-106000-C',
        contracts: 1.5,
        direction: 'buy',
      }, // Taker request
      {
        instrument_name: 'BTC_USDC-29AUG25-106000-C',
        contracts: 1.5,
        direction: 'buy', // Same direction - previously allowed but incorrect
        price: 2500.0,
        // ... other limit order fields
      },
    ],
  ],
};

After:

// Now required (correct market making)
const rfqResponse = {
  fills: [
    [
      {
        instrument_name: 'BTC_USDC-29AUG25-106000-C',
        contracts: 1.5,
        direction: 'buy',
      }, // Taker request
      {
        instrument_name: 'BTC_USDC-29AUG25-106000-C',
        contracts: 1.5,
        direction: 'sell', // Opposite direction - now required
        price: 2500.0,
        // ... other limit order fields
      },
    ],
  ],
};

Benefits:

  • Correct market making: Ensures proper liquidity provision patterns
  • Reduced errors: Prevents mismatched trading pairs that could lead to unintended positions
  • Market integrity: Aligns with traditional finance market making principles

WebSocket Rate Limiting Scope Change

  • Rate limiting changed from per-API-key to per-session for WebSocket connections
  • Independent limits: Each WebSocket connection now has its own 15 requests/minute limit
  • Same endpoints affected: get_ob_state_by_instruments and get_ob_state_by_market

Before:

// All connections using the same API key shared a single rate limit
// If you had 3 WebSocket connections with the same API key,
// they collectively shared 15 requests/minute

After:

// Each WebSocket connection gets its own independent rate limit
// 3 WebSocket connections = 3 × 15 = 45 total requests/minute

Benefits:

  • Better scalability: Multiple connections per API key don't interfere with each other
  • Fairness: Each connection gets full rate limit allocation
  • Simplified management: Rate limits tied to connection lifecycle rather than API key usage

[1.4.0] - 2025-07-26

✨ Added - New Endpoint and Enhanced Features

New REST Endpoint: Index Price Retrieval

  • Added GET /index_price endpoint for retrieving current index prices
    • Supported markets: BTC, ETH, ARB
    • Data source: Real-time Chainlink oracle feeds via WebSocket streaming
    • Response format: Direct numerical value (e.g., 106500.25)
    • Authentication: API key required
    • Use cases: Portfolio valuation, risk management, strategy analysis, settlement reference

WebSocket Rate Limiting Implementation

  • Added per-API-key rate limiting for market data WebSocket messages:
    • get_ob_state_by_instruments: 15 requests per minute per API key
    • get_ob_state_by_market: 15 requests per minute per API key
  • Enhanced error responses with reset time and remaining request information
  • Sliding window algorithm with 60-second windows for fair usage distribution

WebSocket Documentation Enhancements

  • Added comprehensive timestamp format clarification:
    • Event wrapper timestamps (timestamp_ms): 13-digit milliseconds for event ordering
    • Data payload timestamps (timestamp): Usually 13-digit milliseconds for data-specific times
    • Creation timestamps (creation_timestamp): 10-digit seconds for order/request creation

🔧 Changed - Rate Limiting and Performance

WebSocket Rate Limiting Behavior

  • Rate limits now apply per API key instead of global limits
  • Enhanced rate limit responses include specific reset timing and remaining request counts
  • Alternative guidance provided encouraging subscription-based real-time updates over polling

[1.3.0] - 2025-07-17

✨ Added - API Documentation Enhancements

Enhanced API Documentation Structure

  • Added Key Identifier Types section to main API documentation explaining order_id vs trade_id relationship
  • Created comprehensive developer guide (order_id_vs_trade_id.md) with detailed trading scenarios and examples

Enhanced Batch Order Constraints

  • Added detailed constraint documentation for limit order batching:
    • All orders must be from the same maker
    • All orders must be for the same market (BTC, ETH, or ARB)
  • Added comprehensive examples showing valid and invalid batch configurations
  • Enhanced error response documentation with specific rejection reasons and examples

Order Cancellation Requirements

  • Made market field required in CancelAllOrdersRequest schema (breaking change for order cancellation)
  • Updated endpoint description to clarify market-specific cancellation behavior

[1.2.0] - 2025-01-10

⚠️ Removed - WebSocket Method

Deprecated get_last_trades_by_instrument WebSocket Method

The get_last_trades_by_instrument WebSocket method has been removed. This method was previously used to retrieve historical trade data through the WebSocket connection.

Migration Guide

Use the REST API endpoint instead:

// Before (WebSocket)
ws.send(
  JSON.stringify({
    type: 'get_last_trades_by_instrument',
    instrument_name: 'BTC_USDC-29AUG25-106000-C',
    start_timestamp: 1677721600000,
    end_timestamp: 1677808000000,
  }),
);

// After (REST API)
const response = await fetch(
  `/last_trades_by_instrument?instrument_name=${instrumentName}&start_timestamp=${startTimestamp}&end_timestamp=${endTimestamp}`,
  {
    headers: {
      'x-apikey': API_KEY,
      'Content-Type': 'application/json',
    },
  },
);
const trades = await response.json();

Benefits

  • Better Performance: REST API is optimized for historical data queries
  • Caching: REST responses can be cached by CDNs and browsers
  • Simpler Integration: No need to maintain WebSocket connection for historical data
  • Standard HTTP: Use familiar HTTP methods and status codes for error handling

📚 Enhanced - Order Submission Documentation

Improved Code Examples and Field Clarifications

Enhanced the order submission documentation with clearer examples and important field requirements:

  1. Signature Requirements: Clearly documented that both signature andsignature_deadline are required when not using one-click sessions
  2. Separate Examples: Added distinct examples for options and perpetual orders showing the correct field usage
  3. Field Clarifications:
    • Options orders use contracts field
    • Perpetual orders use amount field
    • The taker field is optional and defaults to zero address (any taker)

New Code Examples

// Options Order Example
const optionsOrder = {
  instrument_name: 'BTC_USDC-29AUG25-106000-C',
  type: 'good_til_cancelled',
  contracts: 1.5, // For options
  direction: 'buy',
  price: 2500,
  // ... other fields
};

// Perpetuals Order Example
const perpsOrder = {
  instrument_name: 'BTC_USDC-PERPETUAL',
  type: 'good_til_cancelled',
  amount: 10000, // For perpetuals
  direction: 'buy',
  price: 45000,
  // ... other fields
};

🔧 Enhanced - Error Response Documentation

Detailed Validation Error Structure

Order rejection responses now include detailed validation error information when the rejection reason is "invalid request":

{
  "rejected_orders": [
    {
      "order_id": "abc123",
      "reason": "invalid request",
      "error": [
        {
          "instancePath": "/orders/0/contracts",
          "schemaPath": "#/properties/contracts/minimum",
          "keyword": "minimum",
          "params": { "limit": 0 },
          "message": "must be >= 0"
        }
      ]
    }
  ]
}

Benefits

  • Precise Error Location: instancePath shows exactly which field failed validation
  • Clear Error Messages: Human-readable messages explain what went wrong
  • Faster Debugging: Developers can quickly identify and fix validation issues

[1.1.1] - 2025-01-09

🔧 Fixed - API Header Standardization

API Key Header Casing

Standardized the API key header name to use consistent lowercase formatting:

Before:

X-APIKey: your-api-key-here

After:

x-apikey: your-api-key-here

Migration Guide

Update your API requests to use the lowercase header:

// Before
const response = await fetch('/instruments?market=ETH', {
  headers: {
    'X-APIKey': API_KEY,
    'Content-Type': 'application/json',
  },
});

// After
const response = await fetch('/instruments?market=ETH', {
  headers: {
    'x-apikey': API_KEY,
    'Content-Type': 'application/json',
  },
});

Benefits

  • Consistent Header Casing: All headers now follow lowercase convention
  • HTTP/2 Compatibility: Lowercase headers are required for HTTP/2
  • Framework Alignment: Matches standard practices across modern APIs

🔧 Fixed - WebSocket Event Types

Corrected Event Type Naming

Fixed inconsistent event type naming in WebSocket documentation:

Before:

{
  "type": "withdraw",
  "action": "withdraw"
}

After:

{
  "type": "withdrawal",
  "action": "withdrawal"
}

Migration Guide

Update your WebSocket event handlers:

// Before
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'withdraw') {
    // Handle withdrawal event
  }
};

// After
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'withdrawal') {
    // Handle withdrawal event
  }
};

Benefits

  • Consistency: Event types now match the implementation and enum definitions
  • Clarity: "withdrawal" is more descriptive than "withdraw"
  • Type Safety: Consistent naming improves TypeScript integration

[1.1.0] - 2025-01-04

🚀 Enhanced - Account State Endpoint

Improved Risk & Market Data

The /account_state/{account} endpoint now provides significantly more detailed information to support advanced trading strategies and risk management.

Breaking Changes

  • Response Structure Flattened: The summary wrapper object has been removed. Account state fields are now directly accessible at the root level of each margin account.

    Before:

    {
      "margin_accounts": [
        {
          "pair": "ETH_USDC",
          "summary": {
            "equity": 10500.0,
            "im": 2000.0,
            "mm": 1500.0,
            "pnl": 500.0
          },
          "positions": [...]
        }
      ]
    }

    After:

    {
      "margin_accounts": [
        {
          "pair": "ETH_USDC",
          "timestamp": 1677721600000,
          "margin_account": 12345,
          "equity": 10500.0,
          "im": 2000.0,
          "mm": 1500.0,
          "unrealised_pnl": 500.0,
          // ... additional fields
          "positions": [...]
        }
      ]
    }

New Response Fields

  • timestamp: Unix timestamp when the account state was calculated
  • margin_account: Unique margin account identifier
  • matrix_risk: Portfolio risk matrix component for comprehensive risk assessment
  • delta_risk: Delta risk component measuring directional exposure
  • roll_risk: Roll risk component for near-expiry option positions
  • portfolio_greeks: Portfolio-level Greeks aggregation
    • delta: Portfolio delta exposure
    • gamma: Portfolio gamma exposure
    • vega: Portfolio vega exposure
    • theta: Portfolio theta (time decay)
    • rho: Portfolio rho (interest rate sensitivity)

Enhanced Position Data

Positions now include real-time market data and risk metrics:

For Options:

  • instrument_type: Always "option" for option positions
  • mark_price: Current mark price of the option
  • mark_iv: Mark implied volatility
  • mark_interest: Mark interest rate used in pricing
  • position_greeks: Position-level Greeks
    • delta, gamma, theta, vega, rho

For Perpetuals:

  • instrument_type: Always "perp" for perpetual positions
  • mark_price: Current mark price of the perpetual
  • current_funding_rate: Current funding rate
  • position_greeks: Position-level Greeks
    • delta

Migration Guide

Update your code to access account state fields directly:

// Before
const equity = accountState.margin_accounts[0].summary.equity;
const im = accountState.margin_accounts[0].summary.im;

// After
const equity = accountState.margin_accounts[0].equity;
const im = accountState.margin_accounts[0].im;

// New capabilities
const portfolioGreeks = accountState.margin_accounts[0].portfolio_greeks;
const riskBreakdown = {
  matrix: accountState.margin_accounts[0].matrix_risk,
  delta: accountState.margin_accounts[0].delta_risk,
  roll: accountState.margin_accounts[0].roll_risk,
};

Benefits

  • Better Risk Management: Detailed risk component breakdown enables more sophisticated risk monitoring
  • Real-time Market Data: Position-level mark prices and Greeks for accurate P&L calculation
  • Reduced API Calls: Mark prices and Greeks available directly without additional requests
  • Enhanced Analytics: Portfolio-level Greeks aggregation for portfolio analysis
  • Streamlined Integration: Simplified response structure reduces parsing complexity

For questions or support, please contact our development team or visit our documentation.