Institutional Trading Gateway API Specification

Version: 1.1.7 Last Updated: September 2026
Release Notes: Enhanced FIX protocol integration and added support for dynamically loading FIX session configurations.


Table of Contents

  1. Introduction & Architecture Overview
  2. Getting Started & Integration Prerequisites
  3. Security & Authentication
  4. 3.1 REST API Authentication
  5. 3.2 WebSocket Authentication
  6. Session Management
  7. REST API Reference
  8. 5.1 Push Instruments & FIX Sessions (MANDATORY PREREQUISITE)
  9. 5.2 Retrieve Configured Instruments
  10. 5.3 Place Order
  11. 5.4 Edit Order / Order Cancel Replace
  12. 5.5 Cancel Order
  13. 5.6 Get Order Status
  14. WebSocket Real-Time API
  15. 6.1 Connection & Authentication
  16. 6.1.1 WebSocket Connection and Order Events
  17. 6.2 Channel Subscription
  18. 6.3 Real-Time Order Execution Reports
  19. 6.3.1 Order Execution Reports
  20. 6.3.2 Order Cancel Rejected Report
  21. 6.3.3 Session Configuration Status
  22. 6.4 Real-Time Market Data Quotes
  23. 6.5 Ping / Pong Heartbeat Protocol
  24. Event Replay & Gap Recovery Protocol
  25. Error Codes & Diagnostics
  26. SDK Examples
  27. 9.1 Comprehensive Node.js Client Example
  28. 9.2 Trading API Gateway - Client Integration Guide
  29. 9.3 Authentication Overview
  30. 9.4 Request Signature Generation
  31. 9.5 Timestamp and Nonce Requirements
  32. 9.6 Payload Encryption
  33. 9.7 AES Encryption Example
  34. 9.8 AES Decryption Example
  35. 9.9 REST API Request Flow
  36. 9.10 Example: Place Order Request
  37. 9.11 Example: Get Instrument Information
  38. 9.12 Response Handling
  39. 9.13 WebSocket Connection
  40. 9.14 WebSocket Authentication
  41. 9.15 WebSocket Signature Generation
  42. 9.16 WebSocket Authentication Responses
  43. 9.17 WebSocket Message Encryption
  44. 9.18 WebSocket Heartbeat Mechanism
  45. 9.19 Client Requirement
  46. 9.20 WebSocket Reconnection
  47. 9.21 WebSocket Recovery Flow
  48. 9.22 Client Responsibilities
  49. security

1. Introduction & Architecture Overview

Welcome to the Enterprise Trading Bridge API. This document serves as the definitive, long-term guide for integrating with our high-performance trading infrastructure. The system is designed for institutional-grade trading integrations with encrypted communication, authenticated API access, real-time event streaming, and event recovery mechanisms.

The platform provides two primary interfaces: - REST API (/v1): Used for synchronous operations, dynamic FIX session management, instrument configuration, order routing (Limit, Market, Replace, Cancel), and querying order status. - WebSocket API (/ws): Used for real-time market data quote streaming and continuous order execution report broadcasts.


2. Getting Started & Integration Prerequisites

Follow these steps in order when integrating with the gateway:

  1. Obtain API Credentials: Acquire your API Key, API Secret (for HMAC-SHA256 signature generation), and shared AES Key + AES IV (32-byte key, 16-byte IV for payload encryption).
  2. Whitelist IP Address: Ensure client server IP addresses are whitelisted at the gateway firewall level.
  3. Establish REST / WS Connections:
  4. Base REST URL: http://<HOST>:<PORT>/v1 (e.g., http://192.168.1.145:3120/v1)
  5. WebSocket URL: ws://<HOST>:<PORT>/ws (e.g., ws://192.168.1.145:3120/ws)
  6. [MANDATORY] Push Instrument & Session Mappings: Call POST /v1/instruments before executing any trades. The bridge uses this data to validate price tick sizes, step sizes, and route orders to the correct FIX sessions.

CRITICAL: Orders referencing a symbol not configured via POST /v1/instruments will be rejected with error code 13001 (SYMBOL_NOT_CONFIGURED).


3. Security & Authentication

The gateway enforces strict end-to-end security via HMAC-SHA256 request signing, 5-second window timestamp checking, nonce replay caching, and AES-256-CBC payload encryption.

3.1 REST API Authentication

All protected REST requests must include the following HTTP headers:

Header Name Type Description
x-api-key string Public API Key issued to your account.
x-timestamp string Current 13-digit Unix millisecond timestamp.
x-nonce string Unique random string (minimum 16 hexadecimal characters).
x-signature string HMAC-SHA256 signature calculated over the request components.

Signature Generation Formula

signature = HMAC_SHA256(API_SECRET, API_KEY + TIMESTAMP + NONCE + HTTP_METHOD + REQUEST_PATH + REQUEST_BODY)

Rules: - HTTP_METHOD must be uppercase (e.g., POST, GET, PUT, DELETE). - REQUEST_PATH includes the full path (e.g., /v1/orders). - REQUEST_BODY is the exact wire payload string (e.g., {"encryptedData":"..."}). For GET or DELETE requests without a body, use "" (empty string).

Request Payload Envelope Format (AES-256-CBC)

All JSON request bodies must be encrypted using AES-256-CBC with PKCS7 padding and sent in a base64 envelope:

{
  "encryptedData": "U2FsdGVkX19zomeEncryptedBase64StringRepresetingOrder..."
}

Server responses are returned using the same encrypted envelope format.


3.2 WebSocket Authentication

WebSocket authentication requires sending an auth message immediately after TCP connection:

{
  "type": "auth",
  "apiKey": "<YOUR_API_KEY>",
  "encryptedData": "<Base64 Encrypted JSON Auth Payload>"
}

Authentication Payload (Before AES Encryption)

{
  "timestamp": "1787032498541",
  "nonce": "8f7a2c9d4e1b3f0a9c8d7e6f5a4b3c2d",
  "signature": "<HMAC_SHA256(API_SECRET, API_KEY + timestamp + nonce)>",
  "lastSequence": 0,
  "clientVersion": "1.1.0",
  "platform": "linux"
}

Server Response

{
  "type": "auth.success"
}
  • auth.success – Authentication successful.
  • auth.failure – Authentication failed. Check your credentials, timestamp, nonce, or signature.

4. Session Management

  • REST Interface: Stateless. Every request is independently validated and authenticated.
  • WebSocket Interface: Stateful. Heartbeat (Ping/Pong) frames are monitored. If no Pong is received for some period of time (e.g, 30 sec), the socket will be forcefully disconnected.

5. REST API Reference

REST Endpoint HTTP Method Description
/v1/instruments POST Mandatory setup: push symbols, FIX sessions, trade rules, LP routing.
/v1/instruments GET Retrieve configured instrument mappings.
/v1/orders/place_order POST Place a new order (Market, Limit).
/v1/orders/order_cancel_replace POST Edit or replace an active order (price/qty).
/v1/orders/cancel_order POST Cancel a resting order.
/v1/orders/order_status POST Query active order status and execution state.

5.1 Push Instruments & FIX Sessions (MANDATORY PREREQUISITE)

  • Endpoint: POST /v1/instruments
  • Auth Required: Yes

This endpoint registers dynamic FIX sessions, symbol trading constraints, and LP routing configuration into the gateway engine.

Field Reference — fixSessions Object

Field Type Required Description
sessionId string ✅ Unique identifier for the FIX session (e.g., lp-binance).
type enum ✅ Which refer following session is for trading / market data purpose trading for trade quote for market data.
version string ✅ FIX protocol version used by the session (e.g., FIX.4.4).
connectionRole enum ✅ FIX connection role. Values: initiator or acceptor.
accountId string ✅ Liquidity provider account identifier.
host string ✅ FIX gateway host/IP address.
port integer ✅ FIX gateway connection port.
senderCompId string ✅ FIX SenderCompID value used for session identification.
targetCompId string ✅ FIX TargetCompID value used for session identification.
username string ✅ FIX session username for authentication.
password string ✅ FIX session password for authentication.
ssl string ❌ Indicates whether the FIX gateway supports or requires SSL/TLS. The default value depends on the session type and is true for trading sessions.
use_ssl string ❌ Specifies whether the FIX client should use SSL/TLS when connecting to the configured FIX gateway. The default value depends on the session type and is true for trading sessions.
reset_on_logon string ❌ Specifies whether the FIX session sequence numbers should be reset when the client logs on to the FIX session. The default value depends on the session type; for trading sessions, the default is false
sessionSchedule object ✅ Defines when the FIX session is active.

Field Reference — sessionSchedule Sub-Object

Field Type Required Description
timezone string ✅ Timezone used for session schedule (e.g., UTC).
startDay string ✅ Session start day (e.g., Sunday).
endDay string ✅ Session end day (e.g., Friday).
startTime string ✅ Session start time in HH:mm:ss format.
endTime string ✅ Session end time in HH:mm:ss format.

Field Reference — liquidityProviders Object

Field Type Required Description
lpId string ✅ Unique identifier for the liquidity provider (e.g., BINANCE, LMAX).
name string ✅ Human-readable liquidity provider name (e.g., Binance Institutional).
sessionId string ✅ Reference to the associated FIX session from fixSessions.

Field Reference — symbols Object

Field Type Required Description
symbol string ✅ Broker-side trading symbol identifier (e.g., BTC_USD).
description string ✅ Human-readable instrument description (e.g., Bitcoin CFD).
baseCurrency string ✅ Base currency/asset code (e.g., BTC, ETH).
quoteCurrency string ✅ Quote currency/settlement currency code (e.g., USD).
tradeRules object ✅ Trading constraints and precision rules.
execution object ✅ Supported order execution types.
routing object ✅ Liquidity provider routing configuration.

Field Reference — tradeRules Sub-Object

Field Type Required Description
pricePrecision integer ✅ Number of decimal places supported for prices.
quantityPrecision integer ✅ Number of decimal places supported for quantities.
tickSize number ✅ Minimum price movement increment (e.g., 0.01).
stepSize number ✅ Minimum quantity increment (e.g., 0.00001).
contractSize number ✅ The number of units of the underlying asset represented by one lot (e.g., contractSize = 100000` (EUR/USD)).
minOrderQty number ✅ Minimum allowed order quantity.
maxOrderQty number ✅ Maximum allowed order quantity.

Field Reference — execution Sub-Object

Field Type Required Description
allowMarket boolean ✅ Indicates whether market orders are supported.
allowLimit boolean ✅ Indicates whether limit orders are supported.
allowStop boolean ✅ Indicates whether stop orders are supported.
allowStopLimit boolean ✅ Indicates whether stop-limit orders are supported.

Field Reference — routing Sub-Object

Field Type Required Description
tradeLpId string ✅ Liquidity provider identifier used for order routing. Must match an entry in liquidityProviders.
marketDataLpId string ✅ Liquidity provider identifier used for market data. Must match an entry in liquidityProviders.

Logical Request Example (AES Encrypted)

{
    "fixSessions": [
        {
            "sessionId": "lp-trading",
            "type": "trading",
            "version": "FIX.4.4",
            "connectionRole": "initiator",
            "accountId": "CLIENT_TEM",
            "host": "45.77.136.252",
            "port": 10011,
            "senderCompId": "TD_MCAP_FIX",
            "targetCompId": "CENTROID_SOL",
            "username": "MCAP_FIX",
            "password": "3PUjVGDY",
            "ssl": true,
            "use_ssl": true,
            "reset_on_logon": true,
            "sessionSchedule": {
                "timezone": "UTC",
                "startDay": "Sunday",
                "endDay": "Sunday",
                "startTime": "00:00:05",
                "endTime": "00:00:00"
            }
        },
        {
            "sessionId": "lp-marketdata",
            "type": "quote",
            "version": "FIX.4.4",
            "connectionRole": "initiator",
            "accountId": "CLIENT_TEM",
            "host": "45.77.136.252",
            "port": 10010,
            "senderCompId": "MD_MCAP_FIX",
            "targetCompId": "CENTROID_SOL",
            "username": "MCAP_FIX",
            "password": "3PUjVGDY",
            "ssl": false,
            "use_ssl": false,
            "reset_on_logon": true,
            "sessionSchedule": {
                "timezone": "UTC",
                "startDay": "Sunday",
                "endDay": "Sunday",
                "startTime": "00:00:05",
                "endTime": "00:00:00"
            }
        }
    ],
    "liquidityProviders": [
        {
            "lpId": "CENTROID_TRADE",
            "name": "Centroid Trade Execution LP",
            "sessionId": "lp-trading"
        },
        {
            "lpId": "CENTROID_MD",
            "name": "Centroid Market Data LP",
            "sessionId": "lp-marketdata"
        }
    ],
    "symbols": [
        {
            "symbol": "XAUUSD",
            "description": "Gold Spot / US Dollar",
            "baseCurrency": "XAU",
            "quoteCurrency": "USD",
            "tradeRules": {
                "pricePrecision": 2,
                "quantityPrecision": 2,
                "tickSize": 0.01,
                "stepSize": 0.01,
                "contractSize": 100,
                "minOrderQty": 0.01,
                "maxOrderQty": 100
            },
            "execution": {
                "allowMarket": true,
                "allowLimit": true,
                "allowStop": true,
                "allowStopLimit": true
            },
            "routing": {
                "tradeLpId": "CENTROID_TRADE",
                "marketDataLpId": "CENTROID_MD"
            }
        },
        {
            "symbol": "XAGUSD",
            "description": "Silver Spot / US Dollar",
            "baseCurrency": "XAG",
            "quoteCurrency": "USD",
            "tradeRules": {
                "pricePrecision": 3,
                "quantityPrecision": 2,
                "tickSize": 0.001,
                "stepSize": 0.01,
                "contractSize": 5000,
                "minOrderQty": 0.01,
                "maxOrderQty": 500
            },
            "execution": {
                "allowMarket": true,
                "allowLimit": true,
                "allowStop": true,
                "allowStopLimit": true
            },
            "routing": {
                "tradeLpId": "CENTROID_TRADE",
                "marketDataLpId": "CENTROID_MD"
            }
        },
        {
            "symbol": "XPTUSD",
            "description": "Platinum Spot / US Dollar",
            "baseCurrency": "XPT",
            "quoteCurrency": "USD",
            "tradeRules": {
                "pricePrecision": 2,
                "quantityPrecision": 2,
                "tickSize": 0.01,
                "stepSize": 0.01,
                "contractSize": 100,
                "minOrderQty": 0.01,
                "maxOrderQty": 100
            },
            "execution": {
                "allowMarket": true,
                "allowLimit": true,
                "allowStop": true,
                "allowStopLimit": true
            },
            "routing": {
                "tradeLpId": "CENTROID_TRADE",
                "marketDataLpId": "CENTROID_MD"
            }
        }
    ]
}

⚠️ FIX Sessions — Required Configuration

Important: The bridge requires exactly two FIX sessions.

{
  "fixSessions":[
    {
      .
      .
      "type":"trading"
      .
      .
      .
      .
    },
    {
      .
      .
      "type":"quote"
      .
      .
      .
      .
    }
  ]
}

Trading Session (type = "trading")

Used for the trade session connection.

Quote Session (type = "quote")

Used for the market data session connection.


✅ Required Setup

fixSessions
├── trading  → Trade Session Connection
└── quote    → Market Data Session Connection

5.2 Retrieve Configured Instruments

  • Endpoint: GET /v1/instruments
  • Auth Required: Yes

Returns all instruments and FIX sessions configured for the account.


5.3 Place Order

  • Endpoint: POST /v1/orders/place_order
  • Auth Required: Yes

Submits a new order to the FIX gateway engine.

Field Reference — Order Request

Field Name Type Required Description
symbol string ✅ Configured broker symbol (e.g., XAUUSD).
side string ✅ Order side: buy or sell.
type string ✅ Order type: market, limit.
quantity string ✅ Quantity requested (e.g., "0.01"). Must comply with stepSize, minOrderQty and maxOrderQty.
price string ⚠️ Limit price (e.g., "2400.00"). Required for limit orders.
clientOrderId string ✅ Client-generated unique identifier for tracking the order. Minimum length: 1 Maximum length: 31 characters.
timeInForce string ❌ Order execution duration. Allowed values: gtc, ioc, fok. Default: gtc.
clordlinkid string ❌ Client reference triplet: AccountID-TicketID-GroupName (e.g., 1000-12345-DemoGrp1).
direction string ❌ Position intent: in (Open / Entry) or out (Close / Exit).
idempotencyKey string ✅ Unique key used to prevent duplicate order submissions. Minimum length: 16 Maximum length: 64 characters.

Logical Request Example

{
  "symbol": "XAUUSD",
  "side": "buy",
  "type": "market",
  "quantity": "0.01",
  "price": "3333.14",
  "direction": "in",
  "clientOrderId": "ORDTSTNOS1",
  "clordlinkid": "1000-12345-DemoGrp1",
  "idempotencyKey": "idemp-xau-001",
  "timeInForce": "gtc",
}

Decrypted Success Response (201 Created)

{
  "success": true,
  "data": {
    "clientOrderId": "ORDTSTNOSR1",
    "status": "new order single request received",
    "correlationId": "req-12345"
  }
}

5.4 Edit Order / Order Cancel Replace

  • Endpoint: POST /v1/orders/order_cancel_replace
  • Auth Required: Yes

Field Reference — Replace Order

Field Name Type Required Description
clientOrderId string ✅ Client-generated unique identifier for tracking the order. Minimum length: 1 Maximum length: 31 characters.
originalClientOrderId string ✅ The clientOrderId of the existing order that you want to replace. Use the same clientOrderId that was provided when the original order was placed.
symbol string ✅ Trading symbol (e.g., XAUUSD).
side string ✅ Order side: buy or sell This field cannot be modified.
type string ✅ Order type: limit This field cannot be modified.
quantity string ❌ Updated total order quantity (e.g., "0.02").
price string ❌ Updated limit price (e.g., "2405.00").
timeInForce string ❌ Order execution duration. Allowed values: gtc, ioc, fok. This field cannot be modified.
idempotencyKey string ✅ Unique key used to prevent duplicate order submissions. Minimum length: 16 Maximum length: 64 characters.

Request Example

{
  "symbol": "XAUUSD",
  "side": "buy",
  "type": "market",
  "quantity": "0.11",
  "price": "2000.05",
  "clientOrderId": "ORDOCRTOT1",
  "originalClientOrderId": "OGORDCLID3",
  "timeInForce": "fok",
  "idempotencyKey": "idemp-xau-002"
}

Decrypted Success Response (201 Created)

{
  "success": true,
  "data": {
    "clientOrderId": "ORDOCRTOT1",
    "originalClientOrderId": "OGORDCLID3",
    "status": "order cancel replace request received",
    "correlationId": "req-12345"
  }
}

Order Cancel/Replace Lifecycle

The Order Cancel/Replace operation allows you to modify an existing order.

The operation generates two execution reports:

  1. The original order is cancelled.
  2. The replacement order is accepted.

Important

When sending a Cancel/Replace request, use the existing order's clientOrderId as originalClientOrderId.

The Cancel/Replace request must also contain a new clientOrderId for the replacement order.

Example

Assume the original order was placed with:

clientOrderId: NEWORDSGL11

The order placement response from websocket:

{
  "type": "order.update",
  "data": {
    "CentroidOrderId": "12715023",
    "ExecutionID": "12715023",
    "averageFillPrice": "0.000000",
    "clientOrderId": "NEWORDSGL11",
    "lastFillPrice": "0.000000",
    "lastFillQuantity": "0.000000",
    "orderType": "limit",
    "price": "3333.140000",
    "quantity": "0.010000",
    "remainingQuantity": "0.010000",
    "side": "buy",
    "status": "new",
    "symbol": "XAUUSD",
    "text": "New Request",
    "timeInForce": "gtc",
    "timestamp": 1787581196036,
    "totalFilledQuantity": "0.000000",
    "type": "order.accepted",
    "sequence": 263
  }
}

This is the original order.


1. Send Cancel/Replace Request

When you want to modify this order, use:

originalClientOrderId = NEWORDSGL11
clientOrderId         = CANREPORD11

Here:

  • originalClientOrderId = the clientOrderId of the existing order you want to modify.
  • clientOrderId = a new ID that identifies the replacement order.

2. Original Order Cancelled

After the Cancel/Replace request is processed, you will receive an order.cancelled event for the original order:

{
  "type": "order.update",
  "data": {
    "CentroidOrderId": "12715023",
    "ExecutionID": "12715023",
    "averageFillPrice": "0.000000",
    "clientOrderId": "NEWORDSGL11",
    "lastFillPrice": "0.000000",
    "lastFillQuantity": "0.000000",
    "orderType": "limit",
    "price": "3333.140000",
    "quantity": "0.010000",
    "remainingQuantity": "0.010000",
    "side": "buy",
    "status": "cancelled",
    "symbol": "XAUUSD",
    "text": "Cancelled",
    "timeInForce": "gtc",
    "timestamp": 1787581279110,
    "totalFilledQuantity": "0.000000",
    "type": "order.cancelled",
    "sequence": 264
  }
}

This confirms that the original order has been cancelled.


3. Replacement Order Accepted

You will then receive an order.accepted event for the new order:

{
  "type": "order.update",
  "data": {
    "CentroidOrderId": "12715024",
    "ExecutionID": "12715024",
    "averageFillPrice": "0.000000",
    "clientOrderId": "CANREPORD11",
    "lastFillPrice": "0.000000",
    "lastFillQuantity": "0.000000",
    "orderType": "limit",
    "originalClientOrderId": "NEWORDSGL11",
    "price": "1000.050000",
    "quantity": "0.100000",
    "remainingQuantity": "0.100000",
    "side": "buy",
    "status": "new",
    "symbol": "XAUUSD",
    "text": "New Request",
    "timeInForce": "gtc",
    "timestamp": 1787581279111,
    "totalFilledQuantity": "0.000000",
    "type": "order.accepted",
    "sequence": 265
  }
}

This confirms that the replacement order was successfully created.

The new active order is now:

clientOrderId: CANREPORD11
CentroidOrderId: 12715024

Which clientOrderId Should You Use?

After the replacement order is accepted, use the new clientOrderId for future operations on the order.

Before Cancel/Replace:
NEWORDSGL11

After Successful Cancel/Replace:
CANREPORD11

The relationship between the orders is:

Original Order
    clientOrderId: NEWORDSGL11
          |
          | Cancel/Replace
          v
Replacement Order
    clientOrderId: CANREPORD11
    originalClientOrderId: NEWORDSGL11

If the Cancel/Replace Fails

If the replacement order is rejected, the new clientOrderId is not an active order.

In that case, continue using the original clientOrderId as the order reference.

Cancel/Replace Failed
        |
        v
Continue using:
NEWORDSGL11

Summary

Stage Event Client Order ID
Original order placed order.accepted NEWORDSGL11
Original order cancelled order.cancelled NEWORDSGL11
Replacement accepted order.accepted CANREPORD11

Integration rule: Use the original order's clientOrderId as originalClientOrderId when sending a Cancel/Replace request. After receiving order.accepted for the replacement order, use the new clientOrderId for subsequent operations. If the replacement is rejected, continue using the original clientOrderId.


5.5 Cancel Order

  • Endpoint: POST /v1/orders/cancel_order
  • Auth Required: Yes

Field Reference — Cancel Order

Field Name Type Required Description
clientOrderId string ✅ A unique ID generated by the client to identify the order. Use a new and unique value for each order request. Minimum length: 1 characters. Maximum length: 31 characters.
originalClientOrderId string ✅ The clientOrderId of the existing order that you want to cancel. Use the same clientOrderId that was provided when the original order was placed.
symbol string ✅ The trading instrument for the order, for example XAUUSD.
side string ✅ Order side of the order buy or sell.
idempotencyKey string ✅ A unique key generated by the client to ensure that the same request is not processed more than once if it is submitted multiple times. Minimum length: 16 characters. Maximum length: 64 characters.

Request Example

{
    "symbol": "XAUUSD",
    "side": "buy",
    "clientOrderId": "ORDCANCLEE2",
    "originalClientOrderId": "OGORDTK2",
    "idempotencyKey": "idemp-xau-003",
}

Decrypted Success Response (201 Created)

{
  "success": true,
  "data": {
    "clientOrderId": "ORDCANCLEE2",
    "originalClientOrderId": "OGORDTK2",
    "status": "order cancel replace request received",
    "correlationId": "req-12345"
  }
}

5.6 Get Order Status

  • Endpoint: POST /v1/orders/order_status
  • Auth Required: Yes

Queries the execution state and fill statistics for a specific order.

Field Reference — Cancel Order

Field Name Type Required Description
CentroidOrderId string ✅ The unique order ID assigned by Centroid. This value is returned when the order is created. Use the CentroidOrderId of the order whose status you want to retrieve.
originalClientOrderId string ✅ The clientOrderId of the order whose current status you want to retrieve. Use the same clientOrderId that was provided when the order was originally submitted.
symbol string ✅ The trading instrument associated with the order, for example XAUUSD.
side string ✅ The side of the order. Use buy for a buy order or sell for a sell order.
idempotencyKey string ✅ A unique key generated by the client to prevent the same request from being processed more than once. Minimum length: 16 characters. Maximum length: 64 characters.

Request Example

{
    "CentroidOrderId": "12714448",
    "originalClientOrderId": "ORGCLIID2",
    "symbol": "XAUUSD",
    "side": "buy",
    "idempotencyKey": "idemp-xau-004"
}

Decrypted Response Example

{
    "success": true,
    "data": {
        "CentroidOrderId": "12714448",
        "originalClientOrderId": "ORGCLIID2",
        "status": "order status request received",
        "correlationId": "req-12345"
    }
}

⚠️ Note

Some FIX Business Message Reject responses may not be forwarded to the WebSocket client. As a result, you may occasionally receive no response for a request.

This can happen, for example, when: - An invalid originalOrderId is provided in a Get Order Status request. - An Edit Order or Order Cancel Replace request is sent without making any changes to the order.

Please ensure that you provide a valid originalOrderId and make at least one valid modification when submitting an Edit Order request.


6. WebSocket Real-Time API

6.1 Connection & Authentication

Connect to ws://<HOST>:<PORT>/ws and send the HMAC authenticated auth message as detailed in Section 3.2.

6.1.1 WebSocket Connection and Order Events

Before performing any trading-related operations, make sure your WebSocket connection is established and ready to receive messages.

Important

The REST API response only confirms that your request was received or acknowledged. It does not necessarily represent the final result of the order operation.

All order-related events, including order acceptance, execution, cancellation, rejection, and status updates, are delivered through the WebSocket connection.

Therefore, clients should establish the WebSocket connection before sending any trading-related requests.

Request and Response Flow

The general flow is:

1. Establish WebSocket connection
             |
             v
2. Send trading request through REST
             |
             v
3. REST response
   Request received/acknowledged
             |
             v
4. Wait for WebSocket event
             |
             v
5. Process the actual order result

REST Response

The REST response confirms that the request was received and accepted for processing.

For example:

{
  "success": true,
  "data": {
    "clientOrderId": "ODweIe3sKz45",
    "originalClientOrderId": "ODweIe3sKz43",
    "status": "order cancel replace request received"
  }
}

This response does not mean that the order operation has completed successfully.

WebSocket Response

The actual order status and lifecycle events are delivered through WebSocket.

For example:

{
  "type": "order.update",
  "data": {
    "clientOrderId": "ODweIeIe3sKz45",
    "status": "new",
    "type": "order.accepted"
    .
    .
    .
    .
    .
  }
}

The WebSocket event is the source of the actual order state.

Business Validation Errors

Some business-level validation errors may be returned directly in the REST response.

For example, if a request fails validation because of an invalid order quantity, price, symbol, or other business rule, the REST API may return an error immediately.

Therefore, clients should handle both:

  • REST responses — request acknowledgement and immediate business validation errors.
  • WebSocket events — order lifecycle events and final order status.

Clients should follow this sequence:

Connect to WebSocket
        |
        v
Confirm WebSocket is ready
        |
        v
Send REST trading request
        |
        +----------------------+
        |                      |
        v                      v
REST response            WebSocket event
        |                      |
        |                      v
        |                Actual order status
        |
        v
Handle acknowledgement
or immediate validation error

Important: Always establish the WebSocket connection before submitting orders or other trading-related requests. This ensures that the client is ready to receive the corresponding order events as soon as they are generated.


6.2 Channel Subscription

Clients submit an encrypted JSON subscription payload to subscribe to market data quote feeds:

{
  "type": "subscribe",
  "apiKey": "<YOUR_API_KEY>",
  "encryptedData": "<Base64 Encrypted JSON Payload>"
}

encryptedData Payload (Before AES Encryption)

{
"channels": ["market_data.XAUUSD"]
}

Encrypted responce from server

{
"type":"encrypted_event",
"encryptedData":"uoIHKQ5nEwfFXYnVbmKdCkZNSPIvsaquyg/PW0UqyiZjUlE3a9vPr+msExrL8MfYvPKKrAV/6d9VtGP5PN7KRw=="
}

Decrypted Response

{
  "type": "subscribe.ack",
  "channels": [
    "market_data.EURUSD"
  ]
}

6.3 Real-Time Order Execution Reports

6.3.1 Order Execution Reports

Execution events are pushed in real time whenever an order status changes (new, partially_filled, filled, cancelled, rejected):

Field Type Required Description Example
CentroidOrderId string ✅ The unique order ID assigned to the order by Centroid. "12714788"
ExecutionID string ✅ The unique ID assigned to this specific execution report or event. "12714788"
averageFillPrice string ✅ The average price at which the order has been filled so far. "1000.000000"
clientOrderId string ✅ The client-generated identifier for the order. "CANCELEEORD2"
lastFillPrice string ✅ The execution price of the most recent fill event. "1000.050000"
lastFillQuantity string ✅ The quantity executed in the most recent fill event. "0.100000"
orderType string ✅ The type of order (e.g., market, limit, stop). "limit"
originalClientOrderId string ❌ The client order ID of the original order prior to a cancel/replace operation. "ODweIesjzs86"
price string ✅ The order price specified at submission (limit or stop price). "1000.050000"
quantity string ✅ The original total order quantity submitted by the client. "0.100000"
remainingQuantity string ✅ The portion of the order quantity that remains open and unfilled. "0.000000"
side string ✅ The side of the order, either buy or sell. "buy"
status string ✅ The current lifecycle status of the order (e.g., new, partially_filled, filled, cancelled, rejected). "cancelled"
symbol string ✅ The financial instrument or trading symbol associated with the order. "XAUUSD"
text string ❌ Additional descriptive text, status messages, or reason details associated with the event. "Execution"
timeInForce string ✅ Specifies how long the order remains active (e.g., gtc, ioc, fok). "gtc"
timestamp number ✅ The timestamp indicating when the event occurred, in Unix milliseconds. 1787572272335
totalFilledQuantity string ✅ The cumulative quantity filled for the order across all execution events. "0.100000"
type string ✅ Identifies the event type (e.g., order.new or order.accepted, order.partially_filled, order.filled, order.cancelled, order.rejected). "order.cancelled"
sequence number ✅ The sequential number assigned to this event for ordering and tracking purposes. 257

Encrypted responce from server

{
"type":"encrypted_event",
"encryptedData":"uoIHKQ5nEwfFXYnVbmKdCkZNSPIvsaquyg/PW0UqyiZjUlE3a9vPr+msExrL8MfYvPKKrAV/6d9VtGP5PN7KRw=="
}

Decrypted response

{
  "type": "order.update",
  "data": {
    "CentroidOrderId": "12714449",
    "ExecutionID": "12714449",
    "averageFillPrice": "4645.090000",
    "clientOrderId": "ODweIe3sKz37",
    "lastFillPrice": "4645.090000",
    "lastFillQuantity": "0.100000",
    "orderType": "limit",
    "price": "6000.050000",
    "quantity": "0.100000",
    "remainingQuantity": "0.000000",
    "side": "buy",
    "status": "filled",
    "symbol": "XAUUSD",
    "text": null,
    "timeInForce": "gtc",
    "timestamp": 1787563403708,
    "totalFilledQuantity": "0.100000",
    "type": "order.filled",
    "sequence": 233
  }
}

6.3.2 Order Cancel Rejected Report

If Centroid is unable to process an Order Cancel Request, a order.cancel_rejected event is sent through the WebSocket.

This event indicates that the cancellation request was rejected and the order was not successfully cancelled.

Example

{
  "CentroidOrderId": "N/A",
  "clientOrderId": "SUM",
  "originalClientOrderId": "ODR",
  "rejectReason": "cannot cancel",
  "status": "rejected",
  "timestamp": 1787580215980,
  "type": "order.cancel_rejected",
  "sequence": 260
}

Response Fields

Field Description
CentroidOrderId The Centroid order ID associated with the request. This may be "N/A" when the cancellation request is rejected before an order ID can be associated with it.
clientOrderId The client-generated ID associated with the cancel request.
originalClientOrderId The clientOrderId of the order that the client attempted to cancel.
rejectReason The reason why Centroid rejected the cancellation request.
status Indicates the result of the request. For a rejected cancellation, this value is rejected.
timestamp The time at which the rejection event was generated, represented as a Unix timestamp in milliseconds.
type Identifies the event type. For a rejected cancel request, this value is order.cancel_rejected.
sequence The sequential number assigned to the event.

Important

A order.cancel_rejected event means that the cancel request was not successful.

The order should therefore not be treated as cancelled based on this event.

The rejectReason field provides the reason for the rejection. In the example above:

rejectReason: "cannot cancel"

means that Centroid was unable to cancel the requested order.

Clients should use the originalClientOrderId to identify the order for which the cancellation was requested and handle the order according to its current state.

6.3.3 Session Configuration Status

After a FIX connection configuration is submitted, the system sends a session.configuration event through the WebSocket to report the current status of the configured sessions.

This event allows the client to determine whether the Market Data and Trading sessions are currently active.

Example

{
  "type": "session.configuration",
  "data": {
    "marketDataSessionActive": false,
    "timestamp": 1787586081277,
    "tradingSessionActive": false
  }
}

Response Fields

Field Type Description
marketDataSessionActive boolean Indicates whether the configured Market Data FIX session is currently active. true means the session is connected and active. false means the Market Data session is not currently active.
tradingSessionActive boolean Indicates whether the configured Trading FIX session is currently active. true means the session is connected and active. false means the Trading session is not currently active.
timestamp number The time at which the session status was reported, represented as a Unix timestamp in milliseconds.
type string Identifies the event as a session configuration status event. This value is session.configuration.

Understanding the Session Status

The values of marketDataSessionActive and tradingSessionActive represent the current connection status of each configured FIX session.

Both Sessions Active

{
  "marketDataSessionActive": true,
  "tradingSessionActive": true
}

This indicates that both the Market Data and Trading FIX sessions are currently connected and active.

Trading Session Inactive

{
  "marketDataSessionActive": true,
  "tradingSessionActive": false
}

The Market Data session is active, but the Trading session is currently inactive.

Trading requests should not be submitted until the Trading session becomes active.

Market Data Session Inactive

{
  "marketDataSessionActive": false,
  "tradingSessionActive": true
}

The Trading session is active, but the Market Data session is currently inactive.

Both Sessions Inactive

{
  "marketDataSessionActive": false,
  "tradingSessionActive": false
}

Neither session is currently active.

This can occur when the FIX sessions are not connected or the connection/configuration is not valid.

Important

A value of false only indicates that the corresponding FIX session is not currently active. It does not by itself identify the reason.

If a session remains inactive, verify the FIX connection configuration, including:

  • FIX host and port
  • SenderCompID
  • TargetCompID
  • Username and password
  • SSL settings
  • Session schedule
  • Other required FIX session parameters

Once the configuration is valid and the FIX session successfully connects, the corresponding field will report:

{
  "marketDataSessionActive": true,
  "tradingSessionActive": true
}

6.4 Real-Time Market Data Quotes

Real-time Best Bid / Ask (BBO) market data quotes pushed over WebSocket:

Before Decryption

{"type":"encrypted_event",
"encryptedData":"9rJeKLuTdvePOXYYXD/gj0DuIbVDL6nR9TU9Zi6oJsYrjGgaUALebc4QjCxAQOxRNmonxTkvlRQmbJO/NFFQu6HMREUEmfTpRDpr9CtBWw3G4eIor0+eeZhA4OLdnoX9NlS2SLBFfhbsnvFQWWRuNL7pBhAG/anflZqmQpY/4tSWdcoqjm3Ud5KJ3BZ316kd5ejrR6pxeEsMYuPqoPNWj6DZp5dbt/0QK/Kk2WI4l1joqPapDYx0VcadvcCVplmkuzIVUAO08qkzfSbH5cv5q5aQc+sfvVAcrbTycvABs6w="}

After Decryption

{
  "type": "market.data",
  "channel": "market_data.XAUUSD",
  "data": {
    "symbol": "XAUUSD",
    "bids": [{ "price": "2399.50", "size": "100.0" }],
    "asks": [{ "price": "2400.50", "size": "100.0" }],
    "timestamp": 1787032498575
  }
}

6.5 Ping / Pong Heartbeat Protocol

The Trading Gateway uses the standard WebSocket Ping/Pong mechanism to monitor connection health.

  • The server periodically sends Ping frames.
  • Clients must respond with Pong frames.
  • Most WebSocket client libraries handle Ping/Pong automatically. If your client library does not support this, your application must send a Pong frame in response to every Ping received.
  • If the server does not receive a Pong response within the expected interval, the connection will be closed.

7. Event Replay & Gap Recovery Protocol

To prevent lost events during brief network disconnections, the gateway maintains a monotonically increasing sequence ledger using a persistent event stream.

  1. Submit lastSequence integer in your WebSocket auth payload (e.g., "lastSequence": 149).
  2. If lastSequence < serverCurrentSequence, the server immediately streams missing execution reports:
  3. Event: replay.start
  4. Event: replay.event (Array of missed execution reports)
  5. Event: replay.end (hasMore: false, gapDetected: false)
  6. If gapDetected: true (sequence purged), the client should call POST /v1/orders/order_status via REST to reconcile state.

The Event Replay mechanism is used to recover events that may have been missed during a temporary WebSocket disconnection.

The client must keep track of the last event sequence number it successfully received and processed. This sequence number should be sent in the WebSocket auth payload as lastSequence.

How Event Replay Works

When the client reconnects, the server checks the lastSequence provided by the client against the events currently available in the server's event stream.

The server maintains the most recent 100 events for replay processing. Therefore, a maximum of 100 recent events can be considered during a single replay operation.

For example, if the server currently has events up to sequence 300:

Available event sequence:
1 ... 200 201 202 203 ... 298 299 300
                    └───────────────┘
                       Latest 100

If lastSequence is 255, the server returns events from sequence 256 to 300. If lastSequence is 50, the server can only return the latest 100 available events, which are sequences 201 to 300.

Encrypted response

{
    "type": "encrypted_event",
    "encryptedData": "V9y3E5PDv65bEEcoWd3RWaRLkOIWY83sRrUrKYKxWDY="
}

After decrypted the response

Reply event start

{
  "type": "replay.start"
}

Actual missing events

{
  "type": "replay.event",
  "event": {
    "CentroidOrderId": "12715217",
    "ExecutionID": "12715217",
    "averageFillPrice": "0.000000",
    "clientOrderId": "NEWORDSGL15",
    "lastFillPrice": "0.000000",
    "lastFillQuantity": "0.000000",
    "orderType": "limit",
    "price": "3333.140000",
    "quantity": "0.010000",
    "remainingQuantity": "0.010000",
    "side": "buy",
    "status": "cancelled",
    "symbol": "XAUUSD",
    "text": "Cancelled",
    "timeInForce": "gtc",
    "timestamp": 1787641211017,
    "totalFilledQuantity": "0.000000",
    "type": "order.cancelled",
    "sequence": 289
  }
}

Reply event end

{
  "type": "replay.end",
  "hasMore": false,
  "gapDetected": false
}

8. Error Codes & Diagnostics

Centralized error code classifications:

Category Code Range Description
Authentication 11000 - 11011 Invalid API key, invalid HMAC signature, expired timestamp, or nonce reuse.
Authorization 11012 - 11018 Permission and access control failures.
Validation 12000 - 12001 Invalid request payload or format errors.
Business Logic 13000 - 13008 Trading and account operation failures.
Rate Limiting 14000 Request frequency violations.
WebSocket 16000 - 16002 WebSocket connection and message errors.
System Failure 17000 Internal server or ZMQ engine timeout.

Error Response Envelope

{
  "success": false,
  "error": {
    "code": "13001",
    "message": "Symbol XAUUSD has not been configured. Push instrument configuration via POST /v1/instruments first.",
    "timestamp": "2026-08-18T11:05:00.000Z",
    "correlationId": "req-1787032497"
  }
}

9. SDK Examples

9.1 Comprehensive Node.js Client Example

The following snippet demonstrates how to properly encrypt/decrypt payloads, generate HMAC signatures, place a REST order, and authenticate a WebSocket connection to stream encrypted market data.

9.2 Trading API Gateway - Client Integration Guide

9.2.1 Overview

This document explains how to integrate with the Trading API Gateway.

The gateway supports:

  • REST API authentication using HMAC-SHA256 signatures
  • AES-256-CBC payload encryption
  • AES-256-CBC response decryption
  • WebSocket authentication
  • WebSocket heartbeat monitoring
  • WebSocket reconnect and recovery flow

9.3 Authentication Overview

All REST API requests require authentication headers.

Required headers:

Header Description
x-api-key Client API key
x-timestamp Current timestamp in milliseconds
x-nonce Unique random value generated for each request
x-signature HMAC-SHA256 request signature

Example:

x-api-key: <API_KEY>
x-timestamp: 1720000000000
x-nonce: 8f7a2c9d4e1b...
x-signature: 9d8f7a6c...

9.4 Request Signature Generation

The gateway validates every request using an HMAC-SHA256 signature.

The signature input format is:

API_KEY + TIMESTAMP + NONCE + HTTP_METHOD + REQUEST_PATH + REQUEST_BODY

All values are concatenated without separators.

Example:

tex_test_key1720000000000abc123POST/v1/orders{"encryptedData":"xxxxx"}

The signature is generated:

const signature = crypto
    .createHmac(
        "sha256",
        API_SECRET
    )
    .update(signatureBase, "utf8")
    .digest("hex");

9.5 Timestamp and Nonce Requirements

9.5.1 Timestamp

The timestamp must be the current Unix timestamp in milliseconds.

Example:

1720000000000

The server uses this to prevent replay attacks.


9.5.2 Nonce

The nonce must be unique for every request.

Recommended:

  • Generate using cryptographically secure random bytes.
  • Never reuse the same nonce.

Example:

const nonce =
    crypto.randomBytes(16)
    .toString("hex");

9.6 Payload Encryption

All REST request bodies must be encrypted before sending.

The encryption algorithm:

AES-256-CBC

Encrypted payload format:

{
    "encryptedData":"BASE64_ENCRYPTED_VALUE"
}

Example:

Original payload:

{
    "symbol":"BTC_USD",
    "side":"buy",
    "quantity":"10"
}

Sent payload:

{
    "encryptedData":"g83hd82jd92..."
}

9.7 AES Encryption Example

Example implementation:

function encryptData(text) {

    const cipher =
        crypto.createCipheriv(
            "aes-256-cbc",
            AES_KEY,
            AES_IV
        );

    let encrypted =
        cipher.update(
            text,
            "utf8",
            "base64"
        );

    encrypted += cipher.final("base64");

    return encrypted;
}

9.8 AES Decryption Example

Example implementation:

function decryptData(encryptedText) {

    const decipher =
        crypto.createDecipheriv(
            "aes-256-cbc",
            AES_KEY,
            AES_IV
        );

    let decrypted =
        decipher.update(
            encryptedText,
            "base64",
            "utf8"
        );

    decrypted += decipher.final("utf8");

    return decrypted;
}

The same AES key and IV are used to decrypt responses.


9.9 REST API Request Flow

The request flow is:

Client
 |
 | 1. Create JSON payload
 |
 | 2. Encrypt payload
 |
 | 3. Create signature
 |
 | 4. Add authentication headers
 |
 | 5. Send HTTP request
 |
Gateway
 |
 | 6. Validate signature
 |
 | 7. Decrypt payload
 |
 | 8. Process request
 |
 | 9. Encrypt response
 |
Client

9.10 Example: Place Order Request

Endpoint:

POST /v1/orders/place_order

Request payload:

{
  "symbol": "XAUUSD",
  "side": "buy",
  "type": "market",
  "quantity": "0.01",
  "price": "3333.14",
  "direction": "in",
  "clientOrderId": "ORDTSTNOS1",
  "clordlinkid": "1000-12345-DemoGrp1",
  "idempotencyKey": "idemp-xau-001",
  "timeInForce": "gtc",
}

The payload must be encrypted before sending.


9.11 Example: Get Instrument Information

Endpoint:

GET /v1/instruments

GET requests do not contain a body.

Signature input:

API_KEY + TIMESTAMP + NONCE + GET + /v1/instruments + ""

Example:

tex_test_key1720000000000abc123GET/v1/instruments

9.12 Response Handling

Encrypted responses are returned as:

{
    "encryptedData":"BASE64_VALUE"
}

The client must decrypt the value using:

AES-256-CBC

After decryption, the original JSON response is returned.

Example:

Encrypted:

{
    "encryptedData":"83hd82jd..."
}

After decryption:

{
    "success":true,
    "data":{
        "orderId":"ORD123"
    }
}

9.13 WebSocket Connection

WebSocket endpoint:

ws://<gateway-host>/ws

Example:

ws://127.0.0.1:3120/ws

9.14 WebSocket Authentication

Authentication must be the first message after establishing the WebSocket connection.

Connection flow:

Client                     Gateway

CONNECT  ---------------->

AUTH     ---------------->

         <--------------- auth.success

Authentication payload (refer to the example payload provided above in this documentation (3.2)).

9.15 WebSocket Signature Generation

WebSocket authentication signature uses:

API_KEY + TIMESTAMP + NONCE

Example:

const signatureBase =
    API_KEY +
    timestamp +
    nonce;


const signature =
crypto
.createHmac(
    "sha256",
    API_SECRET
)
.update(signatureBase)
.digest("hex");

9.16 WebSocket Authentication Responses

Successful authentication:

{
    "type":"auth.success"
}

Failed authentication:

{
    "type":"auth.failure",
    "message":"Authentication failed"
}

9.17 WebSocket Message Encryption

{
    "encryptedData":"BASE64_VALUE"
}

The client should decrypt the payload using the configured AES key and IV.


9.18 WebSocket Heartbeat Mechanism

The gateway uses the standard WebSocket Ping/Pong protocol.

Heartbeat flow:

Gateway                     Client

Ping frame  ------------->

             <------------- Pong frame

Ping/Pong frames are WebSocket protocol control frames.

They are not application JSON messages.

Most WebSocket libraries automatically respond with Pong.


9.19 Client Requirement

The client WebSocket library must support RFC6455 Ping/Pong.

If the library does not automatically respond, the client must manually send Pong when receiving a Ping event.

Example:

socket.on(
    "ping",
    () => {
        socket.pong();
    }
);

9.20 WebSocket Reconnection

Clients should implement automatic reconnect handling.

Recommended strategy:

First retry  : 100ms
Second retry : 200ms
Third retry  : 400ms
Fourth retry : 800ms

The delay should increase exponentially to avoid reconnect storms.


9.21 WebSocket Recovery Flow

When reconnecting:

  1. Create a new WebSocket connection.
  2. Authenticate again.
  3. Provide the last successfully processed sequence number.

Example:

{
    "type":"auth",
    "lastSequence":12345
}

The server uses this value to replay missed events.

Recovery flow:

Client reconnects

        |

Authenticate with lastSequence

        |

Server sends:

replay.event
replay.event
replay.event

        |

replay.end

        |

Client resumes live event processing

9.22 Client Responsibilities

The client application must:

  • Generate a new nonce for every request.
  • Generate the correct HMAC signature.
  • Encrypt REST request payloads.
  • Decrypt encrypted responses.
  • Authenticate immediately after WebSocket connection.
  • Support WebSocket Ping/Pong.
  • Automatically reconnect after connection loss.
  • Clients should use exponential backoff for automatic reconnects to prevent reconnect storms.
  • Re-authenticate after reconnect.
  • Restore subscriptions after reconnect.
  • Store the last processed sequence number for event recovery.
  • Provide a valid originalOrderId when requesting order status.
  • When submitting an Edit Order request, ensure that at least one valid order field is modified.

10. Security

The infrastructure enforces stringent security protocols:

  • Authentication: Key-pair requirement for all programmatic access.
  • Encryption: AES-256-CBC for all request and response payloads.
  • Signature (HMAC): Validates integrity of path, method, and body.
  • Timestamp & Nonce: Prevents replay attacks. Requests older than the tolerance window (default 5000ms) are rejected.
  • Replay Prevention: Nonce cache ensures the exact same request cannot be processed twice.
  • TLS: All connections enforce TLS 1.2+ minimum.
  • IP Whitelist: Enforced at the gateway level.
  • Rate Limiting: Centralized sliding-window rate limiting is enforced to control request frequency and prevent excessive or abusive traffic.
  • Best Practices: Rotate keys regularly, use isolated IP whitelists, and log all clientOrderIds locally.