Introduction
The Open Trade REST API is a request/response counterpart to the WebSocket API. Every endpoint here is a direct REST equivalent of a WebSocket channel or command — if you'd rather poll or fire one-off requests instead of holding a persistent socket open, this is the interface to use.
REST Base URL
/hermes-ws-gateway/api/rest/balance — concatenate host + that full path.
Endpoint Groups
/public/. Send the Authorization header on all requests below.
/public/ path (market data, no account-specific data)
GET /public/asset-listGET /public/instrument-listGET /public/order-book-publicGET /public/timeGET /public/candles
/trade-private/ path and account endpoints (account/order data)
POST / PUT / DELETE /trade-private/ordersGET /trade-private/trades-historyGET /trade-private/orders-historyGET /trade-private/active-ordersGET /client-portfolioGET /client-portfolio-historyGET /balance
/yield/ path (Yield contracts; needs the Trade permission)
GET /yield/productPOST /yield/subscriptionsGET /yield/contracts/open,GET /yield/contracts/closedGET /yield/contracts/{contractId}GET /yield/summary,GET /yield/contracts/{contractId}/performanceGET /yield/contracts/{contractId}/redemption-quotePOST /yield/contracts/{contractId}/redeem,POST /yield/contracts/{contractId}/cancel
Versioning & Headers
Every REST call — public or private — must include the X-API-Version header. This lets Open Trade evolve the REST contract without breaking existing integrations.
| Header | Required | Description |
|---|---|---|
| X-API-Version | Yes | API version to target. Current value: 1.0.0 |
| Authorization | Private only | Bearer JWT token, obtained via the authentication flow below. Required on every private endpoint. |
Authentication
Private REST endpoints use the exact same credential exchange as the Private Data API (WebSocket) — obtain your JWT once, then reuse it across both interfaces.
- Login with your API key’s API username and password to obtain a session cookie
- Use the session cookie to obtain a JWT token
- Send the JWT token as a Bearer token in the
Authorizationheader of every private REST request
Authentication Flow
Before You Start: Create an API Key
Create a key under Account → API Keys: name it, choose its permissions and confirm with your second factor. The platform generates the key's API username, password and secret and shows them once, so store all three straight away.
| Credential | Example | Where it goes |
|---|---|---|
| API username | 100123_mybot | The username field in Step 1, and the {api-username} in the Step 2 URL — the same value both times |
| Password | — | The password field in Step 1 |
| Secret | — | The clientSecret header in Step 2 |
The password expires one year after the key is created, and login is refused from then on — create a new key before that date.
Step 1: Login with the API Username and Password
Using POST method, send the API username and password to the login endpoint. Note that the credentials must be sent as form-data with keys in lowercase. This returns a SESSION cookie (not JSESSIONID).
POST https://auth.opentrade.exchange/login
// Form Data (lowercase keys!)
username: {api-username}
password: {password}
/login. Only the SESSION cookie of a successful login can obtain a token in the next step.
Step 2: Obtain JWT Token
Use the SESSION cookie from Step 1 to obtain your JWT token. This is a POST with the client secret sent as a header, not a query parameter, and an empty body.
POST https://auth.opentrade.exchange/auth/jwt/clients/{api-username}/token
// Headers
Cookie: SESSION={session_cookie_from_step_1}
clientSecret: {secret}
// Body: empty
/auth/jwt/clients/ (plural) — not /client/. And note this is a single /auth/, not the doubled /auth/auth/ some older references show.
Complete Authentication Flow with cURL
curl -s --cookie-jar cookies.txt -X POST \
"https://auth.opentrade.exchange/login" \
-d "username=[API_USERNAME]&password=[PASSWORD]" \
-o /dev/null -w "login redirected to %{redirect_url}\n" \
--next -s -b cookies.txt -X POST \
"https://auth.opentrade.exchange/auth/jwt/clients/[API_USERNAME]/token" \
-H "accept: */*" \
-H "clientSecret: [SECRET]" \
-d ""
Bearer eyJ.... Use it as-is in the Authorization header — don't add a second "Bearer " prefix. The response also includes a jwt-expire-at header (when the token expires) and jwt-scope header.
Using Your Token
The token returned from the authentication flow already includes the Bearer prefix (the response body reads Bearer eyJ...). Use it exactly as returned in the Authorization header — do not prepend a second Bearer , or the header will read Bearer Bearer eyJ... and be rejected.
Include it on every private REST call, alongside the required X-API-Version header.
GET /hermes-ws-gateway/api/rest/trade-private/active-orders HTTP/1.1
Host: gateway-public.opentrade.exchange
X-API-Version: 1.0.0
Authorization: Bearer eyJraWQiOiIwYWJmMGYwZS00YzliLTQ3NjItYjkxYS04NjE2YzkxMzJkMjkiLCJhbGciOiJSUzI1NiJ9...
Token Lifetime
The token response includes two headers you should read and store alongside the token itself:
| Header | Description |
|---|---|
| jwt-expire-at | ISO-8601 timestamp of when the token expires. Re-run the login + token exchange before this time to get a fresh token. |
| jwt-scope | Scope granted to this token, e.g. api.access. |
- Store your token securely and never expose it in client-side code
- Read
jwt-expire-atfrom the token response and refresh proactively before that time, rather than waiting for a 401 - Re-running the full login + token exchange is how you refresh — there is no separate refresh-token endpoint
- Use environment variables or a secrets manager to store credentials, never hardcode them in source control
REST equivalent of the WS ASSET_LIST channel. Returns the full list of assets supported across all connected exchanges.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"class": "Currency",
"code": "BTC",
"name": "BTC"
}
]
REST equivalent of the WS INSTRUMENT_LIST channel. Returns all tradable instruments (pairs) and their quote currency.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"class": "Instrument",
"quoteCurrencyCode": "BTC",
"code": "BTC/USD",
"name": "BTC/USD"
}
]
REST equivalent of a WS ORDER_BOOK_PUBLIC channel GET snapshot. Returns an L2 order book snapshot (bids/asks) for the requested instrument(s).
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| instruments | query | array<string> | Yes | Instrument codes to fetch, e.g. BTC/USD |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/public/order-book-public?instruments=BTC/USD HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
[
{
"class": "OrderBook",
"exchange": "OKEX",
"symbol": "BTC/USDT",
"bids": [
[63985.5, 0.50735545]
],
"asks": [
[63985.6, 1.08926571]
],
"eventTime": 1783442361532
}
]
[price, amount] pair, ordered best-price-first. The response returns one entry per exchange with liquidity for the requested instrument — expect multiple entries per instrument (e.g. one each for OKEX, BITGET, HUOBI), not a single merged book.
The Open Trade gateway's clock. Use it as "now" when deciding whether a candle has closed: a candle opened at T with length L has closed once serverTime >= T + L. Candles serves it about two minutes later, so a period that is still missing once serverTime >= T + L + 120000 had no trades.
GET /hermes-ws-gateway/api/rest/public/time HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"serverTime": 1790593200123
}
eventTime on the order book.
Closed OHLCV candles for one pair, newest first. A backtest session serves the same path, parameters and rows for a venue's own trades; pass exchange explicitly so one call works on both.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| instrument | query | string | Yes | Pair code, e.g. LTC/USDT |
| exchange | query | string | Optional | Default OPENTRADE: candles built from public trades on every connected venue, the series the Open Trade chart shows and the reference for SMART orders. Pass a venue code (HUOBI, OKEX, BITGET…) for that venue's own trades |
| period | query | string | Optional | One of ONE_MIN (default), FIVE_MIN, FIFTEEN_MIN, ONE_HOUR, FOUR_HOURS, ONE_DAY |
| to | query | integer | Optional | Epoch milliseconds. Only candles that end at or before to, rounded down to the period, are returned. Default: now. To page back, pass the oldest openTime you already have |
| limit | query | integer | Optional | How many periods to cover, 1 to 1000. Default 100 |
| Authorization | header | string | Yes | Bearer JWT token |
GET /hermes-ws-gateway/api/rest/public/candles?instrument=LTC/USDT&period=ONE_HOUR&limit=300 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
[
[1790589600000, 84.1, 85.0, 83.9, 84.7, 1520.5],
[1790586000000, 83.5, 84.2, 83.4, 84.1, 990.0]
]
[openTime, open, high, low, close, volume]. openTime is the start of the period in epoch milliseconds UTC, and periods line up with UTC boundaries (hours on :00, four hours on 00, 04, 08, 12, 16 and 20 UTC). Prices come from executed public trades, not the book midpoint, and volume is in the base asset. A period with no trades has no row.
REST equivalent of the WS TRADE_PRIVATE channel CREATE command. Accepts an array so multiple orders can be submitted in a single call.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| exchange | string | Optional | Name of exchange to send order to, such as BITFINEX, LMAX, OKX. If not provided, order will be set as SMART |
| globalInstrumentCd | string | Yes | Instrument code, e.g. BTC/USD |
| clientOrderId | string | Optional | Recommend unique Unix timestamp; server will assign one if omitted |
| direction | string | Yes | BUY or SELL |
| orderType | string | Yes | MARKET, LIMIT, POST_ONLY, BUY_STOP, SELL_STOP, STOP_LOSS, TAKE_PROFIT |
| timeInForce | string | Optional | GTC, GTD, GTT, FOK, IOC |
| price | number | Yes | The limit price for LIMIT and POST_ONLY, and the trigger for BUY_STOP and SELL_STOP. For MARKET, STOP_LOSS and TAKE_PROFIT send the current price as a reference |
| sl | number | Optional | Stop loss trigger. Required for STOP_LOSS. On a LIMIT, MARKET, BUY_STOP or SELL_STOP entry it attaches a stop loss. On a TAKE_PROFIT it links a stop loss to it |
| tp | number | Optional | Take profit trigger. Required for TAKE_PROFIT. On a LIMIT, MARKET, BUY_STOP or SELL_STOP entry it attaches a take profit. On a STOP_LOSS it links a take profit to it |
| amount | number | Yes | Order quantity |
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"direction": "SELL",
"orderType": "LIMIT",
"timeInForce": "GTC",
"price": 0.215,
"amount": 1.0
}
]
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"direction": "SELL",
"orderType": "LIMIT",
"timeInForce": "GTC",
"price": 0.215,
"amount": 1.0,
"orderStatus": "NEW",
"exchangeOrderId": "1928374650",
"filledPrice": 0.0,
"filledAmount": 0.0,
"orderDateTime": "2026-07-07T13:04:43.792Z",
"message": "",
"processInstanceId": "string"
}
]
Take profit and stop loss
A LIMIT, MARKET, BUY_STOP or SELL_STOP entry may carry tp, sl or both. With both, the two exits are one-cancels-other. Leave exchange out: take profit and stop loss need smart routing. For a BUY set sl below and tp above the entry price, for a SELL the reverse; for a BUY_STOP or SELL_STOP the entry price is its stop price. The exits arm as the entry fills, sized to what you received. The rules are the same as on the WebSocket TRADE_PRIVATE channel.
To protect a position you already hold, send one STOP_LOSS or TAKE_PROFIT order carrying both sl and tp, with direction set to the side that closes the position and amount to the quantity to protect. The two exits are one-cancels-other and the quantity is reserved once for the pair. Leave exchange out. For a SELL set sl below and tp above the current price, for a BUY the reverse.
clientOrderId must be unique per order. Reusing an ID will be rejected.
REST equivalent of the WS TRADE_PRIVATE channel UPDATE command (order modification — e.g. price/amount changes on a resting order).
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
Uses the same request/response body shape as Create Orders, but requires an existing clientOrderId or exchangeOrderId to identify the order being modified.
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"direction": "SELL",
"orderType": "LIMIT",
"timeInForce": "GTC",
"price": 0.22,
"amount": 1.0
}
]
REST equivalent of the WS TRADE_PRIVATE channel CANCEL command.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
Note that DELETE requests here carry a request body — identify the order(s) to cancel via clientOrderId, exchangeOrderId, and globalInstrumentCd.
- You must provide either
clientOrderIdorexchangeOrderId— at least one is required to identify the order. - If
clientOrderIdis provided, it is used (takes precedence overexchangeOrderId). - If only
exchangeOrderIdis provided, that order is cancelled. - If neither is provided, all orders for the selected pair(s) will be cancelled.
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"exchangeOrderId": "1928374650"
}
]
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"orderStatus": "CANCELLED",
"exchangeOrderId": "1928374650",
"orderDateTime": "2026-07-07T13:04:52.249Z",
"message": ""
}
]
REST equivalent of the WS TRADE_PRIVATE channel active orders GET. Returns all currently open (not filled/cancelled/rejected) orders.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| instruments | query | array<string> | Optional | Filter to specific instruments; omit for all |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USD",
"clientOrderId": "0989876565",
"direction": "SELL",
"orderType": "LIMIT",
"timeInForce": "GTC",
"price": 0.215,
"amount": 1.0,
"orderStatus": "ACTIVE",
"exchangeOrderId": "1928374650",
"filledPrice": 0.0,
"filledAmount": 0.0,
"orderDateTime": "2026-07-07T13:06:29.441Z",
"message": "",
"processInstanceId": "string"
}
]
REST equivalent of the WS TRADE_PRIVATE channel trades history GET. Returns executed trades (fills) within a date range.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| instruments | query | array<string> | Optional | Filter to specific instruments |
| fromDtm | query | string ($date-time) | Optional | Range start (ISO-8601) |
| toDtm | query | string ($date-time) | Optional | Range end (ISO-8601) |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/trade-private/trades-history?instruments=BTC/USDT&fromDtm=2026-06-01T00:00:00&toDtm=2026-07-01T00:00:00 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
[
{
"class": "Order",
"exchange": "OKEX",
"globalInstrumentCd": "BTC/USDT",
"clientOrderId": "11781606906612",
"direction": "BUY",
"orderType": "MARKET",
"timeInForce": "GTC",
"price": 66591.4,
"amount": 0.0002,
"orderStatus": "FILLED",
"exchangeOrderId": "3660939752535318528",
"filledPrice": 66558.0,
"filledAmount": 0.0002,
"orderDateTime": "2026-06-16T10:48:26.747Z",
"message": null,
"processInstanceId": null
}
]
clientOrderId, one per fill (e.g. PARTIALLY_FILLED followed by FILLED). Aggregate by clientOrderId if you need one row per order rather than per fill.
REST equivalent of the WS TRADE_PRIVATE channel orders history GET. Returns historical orders filtered by status and date range.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| instruments | query | array<string> | Yes | Instrument codes to filter by |
| statuses | query | array<string> | Yes | See status values below |
| fromDtm | query | string ($date-time) | Optional | Range start (ISO-8601) |
| toDtm | query | string ($date-time) | Optional | Range end (ISO-8601) |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
Available Status Values
NEW, SUBMITTED, PENDING, ACTIVE, IN_RECONCILIATION, PARTIALLY_FILLED, FILLED, CANCELLED, REJECTED, EXPIRED, FAILED, REJECTED_WRONG_ENV, DUPLICATE, UNKNOWN
One entry per action on your account — a fill, a transfer, a deposit, a withdrawal, a yield movement or a fee — assembled from the accounting ledger. Where /trade-private/trades-history reports what executed on an exchange, the journal reports what it did to your balances: the debits, the credits, the fees charged and the balances each action left behind.
debits, credits and fees list, not three unrelated rows. The postings array always carries the individual ledger rows behind the entry. Debits are negative and credits positive, so the postings of an entry sum to what it did to your balances.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| size | query | integer | Optional | Entries per page. Default: 50, maximum 500. Anything outside 1..500 is rejected with 400 |
| page | query | integer | Optional | Zero-based page number. Default: 0. A negative page is rejected with 400 |
| periodType | query | string | Optional | Default: L30D. See period values below |
| atdPeriodStart | query | string ($date-time) | Optional | Range start (ISO-8601), required when periodType=ATD |
| atdPeriodEnd | query | string ($date-time) | Optional | Range end (ISO-8601), required when periodType=ATD |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
The client is taken from the token, so there is no way to request another account’s journal.
Period Values
L24H, DTD (today), L7D, WTD, L30D, MTD, L6M, L12M, YTD, ATD (the window given by atdPeriodStart and atdPeriodEnd)
Statuses
COMPLETED, FAILED. A failed entry also carries statusDescription with the reason the ledger recorded.
Fee Types
EXCHANGE, BROKER, CLIENT, SWAP, NETWORK
Action Types
BUY, SELL, DEPOSIT, WITHDRAWAL, TRANSFER, YIELD_SUBSCRIPTION, YIELD_HARVEST, YIELD_REDEMPTION, YIELD_MOVEMENT, DAILY_SETTLEMENT, TRADING_FEE, EXCHANGE_FEE, BROKER_FEE, NETWORK_FEE, FINANCING_FEE, ADJUSTMENT
{
"content": [
{
"actionKey": "11788521799959",
"actionType": "BUY",
"actionName": "Bought",
"clientId": 1000123,
"time": "2026-08-31T14:02:11",
"startTime": "2026-08-31T14:02:09",
"baseAsset": "BTC",
"quoteAsset": "USDT",
"quantity": 0.25,
"price": 88012.4,
"route": ["OKEX"],
"debits": [{"asset": "USDT", "amount": -22003.1}],
"credits": [{"asset": "BTC", "amount": 0.25}],
"fees": [{"asset": "USDT", "amount": 11.0, "feeType": "EXCHANGE"}],
"balances": [{"asset": "BTC", "amount": 1.35}],
"status": "COMPLETED",
"statusDescription": null,
"clientOrderId": "a7c1f0e2-5d44-4e21-9f0b-6e2b8f0d1c33",
"transactionGroupId": "4a248fbd-1cd8-3164-b081-26f40e8248ac",
"postings": [
{
"id": 11788521799959,
"clientId": 1000123,
"assetType": "BTC",
"amount": 0.25,
"price": 88012.4,
"priceCurrency": "USDT",
"time": "2026-08-31T14:02:11",
"description": "Buy BTC/USDT",
"transactionType": "BUY",
"operation": "EXECUTE_WITH_UNLOCK",
"origin": "TOMS",
"status": "SUCCESS",
"assetTypeBalance": 1.35,
"priceCurrBalance": 150244.02,
"transactionGroupId": "4a248fbd-1cd8-3164-b081-26f40e8248ac",
"exchange": "OKEX",
"clientOrderId": "a7c1f0e2-5d44-4e21-9f0b-6e2b8f0d1c33"
}
]
}
],
"number": 0,
"size": 50,
"totalElements": 120
}
REST equivalent of the WS CLIENT_PORTFOLIO channel GET snapshot. Returns current holdings by asset type, including exposure, PnL, and available balances.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"class": "ClientPortfolio",
"assetType": "BTC",
"quantity": 0.0019553,
"avgPriceUsd": 90550.7416782,
"index": 88010.42919245128,
"onOrders": 0.0,
"available": 0.0019553,
"marketValue": 172.0867922,
"unrealizedPnl": -4.967073,
"notional": 177.0538652
}
]
avgPriceUsd is the average price paid for the quantity still open, which is your cost basis over that quantity; index is the current mark price used for valuation. notional is that cost basis in USD, so marketValue less notional is unrealizedPnl, which can be negative. quantity and available can be negative for a short position.
Historical counterpart to Client Portfolio — returns portfolio snapshots over a date range instead of the live snapshot.
REST equivalent of the WS BALANCE channel. Returns account balances by currency.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"class": "Balance",
"currencyCode": "BTC",
"balance": 0.0019553,
"available": 0.0019553,
"locked": 0.0
}
]
balance is total holdings for that currency; available is free/withdrawable; locked is held against open orders (balance = available + locked).
REST equivalent of the WS CLIENT_BALANCE_DISTRIBUTION channel GET. Returns where your tokens actually sit: one row per asset per venue, with cost basis, valuation and PnL for that venue.
/balance and /client-portfolio both report the aggregate across every venue, but an order is routed to exactly one exchange and is checked against that venue's balance alone. Read this endpoint to decide where an order can actually be filled, and how much can be withdrawn or transferred off a given venue.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
There are no query parameters. The client is taken from the token, so there is no way to request another account's distribution.
[
{
"class": "ClientBalanceDistribution",
"clientId": 1000123,
"asset": "BTC",
"exchange": "OKEX",
"finalBalance": 0.35,
"costBasisUsd": 30642.7595,
"marketValueUsd": 30803.6502,
"avgPriceUsd": 87550.7414,
"lockedAmount": 0.05,
"availableAmount": 0.3,
"unrealizedPnl": 160.8907,
"returnPercent": 0.525
},
{
"class": "ClientBalanceDistribution",
"clientId": 1000123,
"asset": "BTC",
"exchange": "BITGET",
"finalBalance": 0.12,
"costBasisUsd": 10866.089,
"marketValueUsd": 10561.2515,
"avgPriceUsd": 90550.7417,
"lockedAmount": 0.0,
"availableAmount": 0.12,
"unrealizedPnl": -304.8375,
"returnPercent": -2.8054
}
]
Response Fields
| Field | Type | Description |
|---|---|---|
| clientId | number | Your Open Trade client identifier, taken from the token. |
| asset | string | Asset code, e.g. BTC, USDT, GALA. |
| exchange | string | Venue holding the asset: OKEX, HUOBI, BITGET, BITMART, BINANCE, DERIBIT, LMAX, GEMINI, BLOCKCHAIN, OPENTRADE. |
| finalBalance | number | Total amount of the asset held on that venue. |
| availableAmount | number | Amount free to trade, withdraw or transfer from that venue. |
| lockedAmount | number | Amount reserved on that venue, typically against open orders or a pending transfer. |
| avgPriceUsd | number | Average price paid for the quantity still open on that venue, in USD. Equal to costBasisUsd divided by finalBalance. |
| costBasisUsd | number | USD paid for the quantity still open on that venue. Anything already closed is excluded. |
| marketValueUsd | number | Current USD market value of the holding on that venue. |
| unrealizedPnl | number | Unrealized PnL on the holding, in USD, measured as market value less cost basis. Negative when the position is under water, and zero when nothing was paid for the holding. |
| returnPercent | number | Return on the holding, in percent, measured against the cost basis. Zero when nothing was paid for the holding. |
An asset held on three exchanges comes back as three rows. Sum finalBalance over the rows sharing an asset to reconcile against /balance, and never assume a single row carries the whole position.
CLIENT_BALANCE_DISTRIBUTION channel returns, carrying the same "class" discriminator. Only the envelope differs: the WS message wraps them in a data array, while REST returns the array on its own.
REST equivalent of the WS DEPOSIT_WALLET channel. Returns the client's deposit wallet addresses, one per supported asset.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
[
{
"class": "DepositWallet",
"id": 1000003606,
"clientId": 100882,
"symbol": "XRP",
"walletAddress": "rnSffMjADGXG9apHR6gjKz8THc1FtZGCov",
"status": "ACTIVE",
"comment": "tag: 1000003606",
"exchange": "OPENTRADE"
},
{
"class": "DepositWallet",
"id": 1000003615,
"clientId": 100882,
"symbol": "AAVE",
"walletAddress": "0x649590c78f702ed0cdeab07daf722673d2510afe",
"status": "ACTIVE",
"comment": null,
"exchange": "OPENTRADE"
}
]
comment carries the destination tag/memo when the asset requires one (e.g. XRP); otherwise null.
REST equivalent of the WS DEPOSIT_WORKFLOW channel GET. Tracks the lifecycle of incoming on-chain deposits as they are detected, confirmed, and credited to your account. /active returns the deposits still in progress as a plain list and takes no parameters; /completed returns the finished ones a page at a time.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| removeFailed | query | boolean | Optional | Excludes failed/errored processes when true. Default false. Completed only |
| dateFrom | query | string ($date) | Optional | Range start (YYYY-MM-DD). Completed only |
| dateTo | query | string ($date) | Optional | Range end (YYYY-MM-DD). Completed only |
| page | query | integer | Optional | Zero-indexed page number. Default 0. Completed only |
| pageSize | query | integer | Optional | Results per page, 1 to 200. Default 50. Completed only |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/deposit/processes/completed?removeFailed=true&dateFrom=2026-01-01&dateTo=2026-07-14&page=0&pageSize=50 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
/active body; items of a /completed page)
[
{
"walletAddress": "0x649590c78f702ed0cdeab07daf722673d2510afe",
"cryptoAssetCode": "USDT",
"transferAmount": 20.0002,
"feeAmount": 0.00001613542206867,
"feeAssetCode": "ETH",
"walletTransferId": "a31a5f77-1826-4e20-a416-ba282c4fd864",
"tag": null,
"processInstanceId": "96ece4ff-463e-11f1-af10-22add62dcf14",
"state": "Update Transfer Status - Complete",
"message": null,
"startTime": "2026-05-02T15:50:05.922Z",
"endTime": "2026-05-02T15:53:27.104Z",
"suspended": false,
"tokenPriceUsd": 0.9997792134,
"errorMessage": null
},
{
"walletAddress": "0x649590c78f702ed0cdeab07daf722673d2510afe",
"cryptoAssetCode": "ETH",
"transferAmount": 0.000811907277365572,
"feeAmount": null,
"feeAssetCode": null,
"walletTransferId": null,
"tag": null,
"processInstanceId": "b6a8edc5-44f0-11f1-bce8-7a2b3ca44423",
"state": "Check Deposit Amount",
"message": null,
"startTime": "2026-05-01T00:00:07.255Z",
"endTime": "2026-05-01T00:00:08.580Z",
"suspended": false,
"tokenPriceUsd": 2255.9838392777,
"errorMessage": null
}
]
/completed returns a page: { "items": [...], "page": 0, "pageSize": 50, "totalItems": 2, "totalPages": 1 }. Its items carry the fields shown above; /active items also carry the processing detail of a deposit still in flight: hotWalletAddress, retryCount, maxRetries, batchId, batchTransactionId, batchStatus and tasks.
state reflects the current step in the deposit pipeline; a state ending in Complete indicates the deposit has been credited. feeAmount, feeAssetCode, and walletTransferId populate once the deposit has been processed on-chain.
REST equivalent of the WS TRANSFER_FEE_QUOTE channel GET. Prices a transfer or withdrawal before it is created: the venue and network fees it will be charged, and what will reach the destination. Show it to the client, then pass its feeAmount as confirmedFee on Create Transfer.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| cryptoAssetCode | query | string | Yes | Asset to transfer, e.g. USDT |
| amount | query | number | Yes | Whole amount to be debited. Must be greater than zero; a missing asset, venue or amount is rejected with 400 |
| fromExchange | query | string | Yes | Source venue code, e.g. OKEX or OPENTRADE |
| toExchange | query | string | Yes | Destination venue code, or BLOCKCHAIN for a withdrawal |
| walletToAddress | query | string | Optional | Destination address of a withdrawal |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/transfer/fee-quote?cryptoAssetCode=USDT&amount=15&fromExchange=OKEX&toExchange=BLOCKCHAIN&walletToAddress=0x1c93969dce56d7eeeb00679dcfe89d7e0f719979 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"class": "TransferFeeQuote",
"cryptoAssetCode": "USDT",
"amount": 15,
"fromExchange": "OKEX",
"toExchange": "BLOCKCHAIN",
"quoted": true,
"sendable": true,
"feeAssetCode": "USDT",
"feeAmount": 1.35,
"venueFee": 1,
"networkFee": 0.35,
"receiveAmount": 13.65,
"minimumAmount": 2,
"refusal": null,
"reason": null
}
Quote Fields
| Field | Type | Description |
|---|---|---|
| quoted | boolean | false when the venue or network could not price the fee right now; reason says why |
| sendable | boolean | false when the transfer would be refused, for example because the amount does not cover the fee and the venue's minimum; refusal says why |
| feeAssetCode | string | Asset the fee is charged in — the transferred asset |
| feeAmount | number | Total fee, venueFee plus networkFee. Send it back as confirmedFee when you create the transfer |
| venueFee | number | Withdrawal fee of the source venue, null when the transfer does not leave a venue |
| networkFee | number | Network fee of the on-chain send from the platform wallet, null when there is none |
| receiveAmount | number | What reaches the destination: amount less feeAmount, rounded down to what the venue can send |
| minimumAmount | number | Smallest amount the source venue will send |
| refusal | string | Why the transfer is not sendable, null when it is |
| reason | string | Why the fee could not be quoted, null when it was |
REST equivalent of the WS TRANSFER_WORKFLOW channel CREATE command. Initiates a transfer either between two connected exchanges/venues, or from an exchange out to an external blockchain wallet address.
Headers
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| fromExchange | string | Yes | Source venue code, e.g. OPENTRADE, OKEX |
| toExchange | string | Yes | Destination venue code, or BLOCKCHAIN to send to an external wallet |
| walletToAddress | string | Optional | Required only when toExchange is BLOCKCHAIN |
| cryptoAssetCode | string | Yes | Asset to transfer, e.g. USDT |
| transferAmount | number | Yes | Whole amount to debit; the fees come out of it |
| confirmedFee | number | Optional | The feeAmount of the fee quote the client confirmed. The transfer is never charged more, and is refused with 409 if the fee has risen |
transferAmount is the whole amount debited from your balance. The venue's withdrawal fee and the network fee are taken out of it and the destination receives the rest. Quote it first with Transfer Fee Quote and pass its feeAmount as confirmedFee: the transfer is then never charged more than the client confirmed, and is refused rather than sent if the fee has risen. Without confirmedFee the fee is priced again as the funds are sent and charged at that price, which may be higher than the quote, and when no fee can be quoted it is sent in full and the venue may charge its fees afterwards.
Exchange to Exchange
{
"fromExchange": "OPENTRADE",
"toExchange": "BITMART",
"cryptoAssetCode": "USDT",
"transferAmount": 4
}
Exchange to Blockchain Wallet (Withdrawal)
This is the same POST /hermes-ws-gateway/api/rest/transfer/start endpoint used above — a withdrawal is simply a transfer where toExchange is set to BLOCKCHAIN and walletToAddress is the external destination address.
{
"fromExchange": "OKEX",
"toExchange": "BLOCKCHAIN",
"walletToAddress": "0x1c93969dce56d7eeeb00679dcfe89d7e0f719979",
"cryptoAssetCode": "USDT",
"transferAmount": 15,
"confirmedFee": 1.35
}
walletToAddress and cryptoAssetCode before submitting.
/transfer/processes/active (in flight) or /transfer/processes/completed (finished) to follow the withdrawal's progress. The response's txId is populated once the withdrawal is broadcast on-chain.
Fee Refusals (409 Conflict)
| Code | Description |
|---|---|
| FEE_CHANGED | The fee has risen above confirmedFee. Quote again and ask the client to confirm the new fee |
| FEE_UNAVAILABLE | confirmedFee was sent but the fee cannot be quoted right now. Retry later |
| FEE_REFUSED | The amount does not cover the fee and the venue's minimum |
A refused transfer starts nothing and locks nothing. Each fee is priced again just before its leg is sent. If the venue's fee has risen above the one quoted at start (above confirmedFee itself when the quote has no networkFee), or the network fee above what confirmedFee leaves for it before anything has left the venue, the transfer ends without sending, your balance is released, and it appears among the completed transfers with its errorMessage. Once the funds have left the venue, a network fee over that is charged at the limit and the platform bears the rest.
{
"class": "TransferWorkflow",
"state": "FEE_CHANGED",
"message": "The fee on this transfer is now 1.6 USDT, above the 1.35 USDT confirmed"
}
REST equivalent of the WS TRANSFER_WORKFLOW channel GET. Tracks the lifecycle of transfers created via Create Transfer, whether between exchanges or out to a blockchain wallet. /active returns the transfers still in progress as a plain list and takes no parameters; /completed returns the finished ones a page at a time.
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| removeFailed | query | boolean | Optional | Excludes failed/errored processes when true. Default false. Completed only |
| dateFrom | query | string ($date) | Optional | Range start (YYYY-MM-DD). Completed only |
| dateTo | query | string ($date) | Optional | Range end (YYYY-MM-DD). Completed only |
| page | query | integer | Optional | Zero-indexed page number. Default 0. Completed only |
| pageSize | query | integer | Optional | Results per page, 1 to 200. Default 50. Completed only |
| transferSubType | query | string | Optional | ALL (default), TRANSFER_ONLY for transfers between venues, or WITHDRAWAL_ONLY for withdrawals to a blockchain address. Completed only |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/transfer/processes/completed?removeFailed=true&dateFrom=2026-07-01&dateTo=2026-07-14&page=0&pageSize=50&transferSubType=ALL HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
/active body; items of a /completed page)
[
{
"fromExchange": "OPENTRADE",
"toExchange": "OKEX",
"cryptoAssetCode": "USDT",
"transferAmount": 5,
"confirmedFee": null,
"walletTransferId": "ffa280b4-3c95-4741-bb25-d154193b6510",
"tag": null,
"processInstanceId": "b0d4b1a1-78ec-11f1-86a3-b6505867aca1",
"state": "Completed",
"message": null,
"startTime": "2026-07-06T03:42:19.954Z",
"endTime": "2026-07-15T18:36:19.707Z",
"suspended": false,
"walletFromAddress": "0x375b9d792d38105bee4b87f72941a6af071d9218",
"feeAmount": 0.00001031880686044,
"feeAssetCode": "ETH",
"txId": "0x205cce2e2077c709a2c9f2b8fffe8861b30c2421bce308bdc87e662d8f90edfb",
"exchangeTxId": null,
"exchangeTransferId": null,
"errorMessage": null,
"tasks": null
},
{
"fromExchange": "OKEX",
"toExchange": "OPENTRADE",
"cryptoAssetCode": "USDT",
"transferAmount": 5,
"confirmedFee": null,
"walletTransferId": "916e31ee-700b-4e60-b6c2-9d14ad651fe9",
"tag": null,
"processInstanceId": "fe8858c3-78dd-11f1-86a3-b6505867aca1",
"state": "Completed",
"message": null,
"startTime": "2026-07-06T01:57:07.865Z",
"endTime": "2026-07-06T01:59:28.529Z",
"suspended": false,
"walletFromAddress": "OKEX",
"feeAmount": 0.097,
"feeAssetCode": "USDT",
"txId": null,
"exchangeTxId": "413425468",
"exchangeTransferId": "413425468",
"errorMessage": null,
"tasks": null
}
]
/active returns these objects as a plain list; /completed returns the same objects in a page: { "items": [...], "page": 0, "pageSize": 50, "totalItems": 2, "totalPages": 1 }.
confirmedFee is always null on these two endpoints; the fee a client confirmed is not read back here. walletFromAddress holds the source wallet address for on-chain transfers, or the source exchange name for exchange-internal transfers. txId is populated only for blockchain transfers; exchangeTxId/exchangeTransferId only for exchange-to-exchange transfers.
Yield
Yield earns the funding rate on a perpetual swap while holding the matching spot position, so the contract carries no net price exposure. You subscribe an amount of the quote asset, the platform buys the base asset spot and shorts the same quantity on the perpetual, and the funding it collects is credited to you daily, less the performance fee. A contract runs until you redeem it.
status and step.
The terms a subscription must meet, and how much you can subscribe from each funding venue right now.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/yield/product HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"enabled": true,
"quoteAsset": "USDT",
"baseAssets": ["DOGE"],
"minAmount": 50,
"maxAmount": 100000,
"performanceFeePercent": 15,
"venue": "HTX",
"headlineApyPercent": 10.5,
"fundingVenues": [
{
"exchange": "HUOBI",
"freeBalance": 1250.4,
"maxSubscribable": 1250.4
}
]
}
Product Fields
| Field | Type | Description |
|---|---|---|
| enabled | boolean | false while Yield is closed to new subscriptions; open contracts keep running |
| quoteAsset | string | Asset you subscribe and are paid in |
| baseAssets | array | Assets a contract can be built on |
| minAmount / maxAmount | number | Limits on one subscription, in quoteAsset |
| performanceFeePercent | number | Share of the gross funding income kept as the performance fee |
| venue | string | Where the strategy runs |
| headlineApyPercent | number | Indicative annual rate, when one is published; not a promise |
| fundingVenues | array | Per venue you can fund from: its freeBalance in quoteAsset and the maxSubscribable amount after the limits |
Starts a contract with the amount you name, taken from one funding venue. Send a clientRequestId of your own on every subscription: a repeat of the same request, after a timeout say, returns the contract the first one started instead of starting a second.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| baseAsset | string | Yes | One of the product's baseAssets, e.g. DOGE |
| amount | number | Yes | Principal in quoteAsset, between minAmount and maxAmount |
| sourceExchange | string | Optional | Funding venue, one of fundingVenues. Defaults to the product's funding venue |
| clientRequestId | string | Optional | Your idempotency key, up to 64 characters |
POST /hermes-ws-gateway/api/rest/yield/subscriptions HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"baseAsset": "DOGE",
"amount": 1000,
"sourceExchange": "HUOBI",
"clientRequestId": "bot-2026-09-22-001"
}
{
"contractId": "7c1d5e2a-5b8e-11f1-9a51-0242ac120004",
"created": true
}
202 means a new contract was started. 200 with "created": false means this clientRequestId had already started the contract named. A refused subscription starts nothing and moves nothing, and answers with the status below and a body of refusal and message:
{
"created": false,
"refusal": "INSUFFICIENT_FUNDS",
"message": "Only 420 USDT is free on HUOBI"
}
Refusal Codes
| refusal | HTTP | Meaning |
|---|---|---|
| PRODUCT_DISABLED | 409 | Yield is not open for subscriptions |
| INVALID_REQUEST | 400 | A required field is missing or malformed, e.g. no asset, a non-positive amount or a clientRequestId over 64 characters |
| ASSET_NOT_OFFERED | 409 | The asset is not in baseAssets |
| VENUE_NOT_OFFERED | 409 | sourceExchange is not one of fundingVenues |
| BELOW_MINIMUM / ABOVE_MAXIMUM | 409 | The amount is outside minAmount–maxAmount |
| INSUFFICIENT_FUNDS | 409 | The funding venue's free balance does not cover the amount |
| CONTRACT_NOT_FOUND | 404 | No contract of yours has that id |
| NOT_REDEEMABLE | 409 | The contract cannot be redeemed now; message names its stage |
| NOT_CANCELLABLE | 409 | The contract is past subscribing and can only be redeemed |
/open returns every contract that has not settled, as a plain list. /closed returns the settled ones — redeemed, matured, cancelled or failed — a page at a time, newest first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| dateFrom | query | string | Optional | Closed only. ISO date; contracts started on or after it |
| dateTo | query | string | Optional | Closed only. ISO date; contracts started on or before it |
| page | query | integer | Optional | Closed only. Zero-based page, default 0 |
| pageSize | query | integer | Optional | Closed only. Default 50, at most 200; outside that is rejected with 400 |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/yield/contracts/closed?page=0&pageSize=50 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Each entry is a contract as Get Contract returns it; /closed wraps them as { "items": [...], "page": 0, "pageSize": 50, "totalItems": 3, "totalPages": 1 }.
One contract, with its legs, its income so far, the income accrued since the last credit, and each credit. Answers 404 for an id that is not one of your contracts.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| contractId | path | string | Yes | The contract's contractId |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/yield/contracts/7c1d5e2a-5b8e-11f1-9a51-0242ac120004 HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"contractId": "7c1d5e2a-5b8e-11f1-9a51-0242ac120004",
"clientId": 24240001,
"clientRequestId": "bot-2026-09-22-001",
"baseAsset": "DOGE",
"quoteAsset": "USDT",
"contractCode": "DOGE-USD",
"status": "ACTIVE",
"statusLabel": "Active",
"step": "Active — earning",
"redeemable": true,
"cancellable": false,
"redemptionRequested": false,
"fundingVenue": "HUOBI",
"venue": "HUOBI",
"redemptionWithdrawsFromVenue": false,
"principal": 1000,
"performanceFeePercent": 15,
"openedAt": "2026-09-01T09:14:05",
"spotFilledQty": 4012,
"spotAvgPrice": 0.2489,
"perpContracts": 4012,
"perpAvgPrice": 0.2491,
"cumulativeYieldGross": 9.41,
"cumulativeFees": 1.41,
"cumulativeYieldNet": 8,
"pendingYieldBase": 2.5,
"pendingYieldQuote": 0.62,
"pendingYieldNet": 0.527,
"lastHarvestAt": "2026-09-22T00:10:00",
"credits": [
{
"creditedAt": "2026-09-22T00:10:00",
"grossAmount": 0.47,
"feeAmount": 0.07,
"netAmount": 0.4,
"baseAssetAmount": 1.89
}
]
}
Contract Fields
| Field | Type | Description |
|---|---|---|
| contractId | string | Identifier of the contract; use it on every per-contract endpoint |
| clientRequestId | string | The idempotency key the subscription was made with, null when none was sent |
| status | string | SUBSCRIBING, ACTIVE, HARVESTING, REDEEMING, PENDING_UNWIND, CLOSED, CANCELLED or FAILED |
| contractCode | string | The perpetual the hedge is held in, e.g. DOGE-USD; its second part is the currency perpAvgPrice is quoted in |
| statusLabel | string | The status in client wording, as the web app shows it |
| step | string | What the contract is doing right now, in client wording; null once it has settled |
| redeemable | boolean | true while the contract is earning and no redemption has been asked for |
| cancellable | boolean | true while the contract is still being built and no cancellation has been asked for |
| redemptionRequested | boolean | A redemption or cancellation has been asked for and is being worked |
| fundingVenue | string | Venue the principal was taken from |
| venue | string | Venue the strategy runs on |
| redemptionWithdrawsFromVenue | boolean | true when a redemption comes back through a venue withdrawal, which carries that venue's fee |
| principal | number | Amount subscribed, in quoteAsset |
| performanceFeePercent | number | Share of the gross funding income kept as the performance fee |
| openedAt / closedAt / cancelledAt | string | When the contract started earning, settled, or was cancelled (UTC) |
| subscriptionTransferFee | number | Fee taken from the principal to move it to the venue, when it had to move |
| spotFilledQty / spotAvgPrice | number | The long spot leg: quantity bought and its average price |
| perpContracts / perpAvgPrice | number | The short perpetual leg: size and its average price |
| cumulativeYieldGross / cumulativeFees / cumulativeYieldNet | number | Funding income credited so far, the performance fee on it, and what reached you, in quoteAsset |
| pendingYieldBase / pendingYieldQuote | number | Income accrued since the last credit and not yet sold, in the base asset and its current quoteAsset value |
| pendingYieldNet | number | pendingYieldQuote less the performance fee: what the pending income would add to your balance |
| lastHarvestAt | string | When income was last credited |
| credits | array | One entry per credit: creditedAt, grossAmount, feeAmount, netAmount, baseAssetAmount |
| redemptionAmount / redemptionTransferFee | number | What a settled contract returned to you and the venue withdrawal fee taken from it |
| redemptionReason | string | CLOSE, CANCEL or MATURITY once a redemption has started |
| txId | string | On-chain transaction of the principal's move to the venue, when it had to move |
/summary totals every contract you have held; /contracts/{contractId}/performance gives the same figures for one contract. Both are what the web app's Yield page shows.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| contractId | path | string | Yes | Contract performance only |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/yield/summary HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"openPrincipal": 1000,
"currentValue": 1008.527,
"lifetimeIncome": 8,
"lifetimeFees": 1.41,
"openContracts": 1,
"deployedContracts": 1,
"apyPercent": 13.9,
"pendingByAsset": { "DOGE": 2.5 },
"pendingValue": 0.527,
"pendingUnreadable": false,
"estimatedDays": false,
"nav": [
{ "date": "2026-09-21", "value": 1007.6, "income": 0.41 },
{ "date": "2026-09-22", "value": 1008.53, "income": 0.4 }
]
}
Performance Fields
| Field | Type | Description |
|---|---|---|
| openPrincipal | number | Principal of the contracts still open |
| currentValue | number | openPrincipal plus the income they have earned, credited and pending, net of the performance fee |
| lifetimeIncome / lifetimeFees | number | Net income credited and performance fees charged, over every contract |
| openContracts / deployedContracts | integer | Contracts still open, and contracts that ever put principal to work |
| apyPercent | number | Annualised return on the principal-days deployed; null until a week of history exists |
| pendingByAsset / pendingValue | object / number | Income accrued since the last credit: gross per base asset, and in quoteAsset net of the performance fee |
| pendingUnreadable | boolean | true when pending income could not be read just now; it is then left out of the figures |
| estimatedDays | boolean | true when some income could not be placed on the day it was earned and is spread evenly over the contract's life, so the daily figures in nav are partly estimated |
| nav | array | Daily series over up to two years: the date, the position value at the end of it, and the income earned that day |
The fee a redemption would take from what it returns. When redemptionWithdrawsFromVenue is false the funds come back without a withdrawal and feeAmount is 0; otherwise it is the venue's current withdrawal fee, in the same shape as Transfer Fee Quote. The fee is charged at the price in force when the funds leave the venue, which may differ from this quote.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| contractId | path | string | Yes | The contract's contractId |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
GET /hermes-ws-gateway/api/rest/yield/contracts/7c1d5e2a-5b8e-11f1-9a51-0242ac120004/redemption-quote HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"cryptoAssetCode": "USDT",
"amount": 1000,
"fromExchange": "HUOBI",
"toExchange": "OPENTRADE",
"quoted": true,
"sendable": true,
"feeAssetCode": "USDT",
"feeAmount": 0
}
/redeem closes an earning contract: both legs are unwound at market and the principal plus any income not yet credited comes back to your balance, less the fee in Redemption Fee Quote. /cancel stops a contract that is still being built and returns what had been deployed. Check redeemable or cancellable on the contract first. Neither can be undone.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| contractId | path | string | Yes | The contract's contractId |
| Authorization | header | string | Yes | Bearer JWT token |
| X-API-Version | header | string | Optional | Default: 1.0.0 |
POST /hermes-ws-gateway/api/rest/yield/contracts/7c1d5e2a-5b8e-11f1-9a51-0242ac120004/redeem HTTP/1.1
X-API-Version: 1.0.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"contractId": "7c1d5e2a-5b8e-11f1-9a51-0242ac120004",
"contract": { "status": "ACTIVE", "redemptionRequested": true, ... }
}
202 means the request was accepted; contract is the contract as it now stands. A contract that cannot be redeemed or cancelled now answers 409 with NOT_REDEEMABLE or NOT_CANCELLABLE, and an id that is not yours answers 404 with CONTRACT_NOT_FOUND — see Refusal Codes.
{
"contractId": "7c1d5e2a-5b8e-11f1-9a51-0242ac120004",
"refusal": "NOT_REDEEMABLE",
"message": "The contract is subscribing and cannot be redeemed now"
}