VERSION 3.7 (see Revision History)
1. Authentification
PT_ACCESS_TOKEN - JWT, see credential_secret rule:
1.1. Authentication
PowerTrade requires clients to use API keys in order to access the APIs.
There are two components to an API key: the key itself and an API secret_key. Both are needed in order to authenticate and use API services.
To establish a connection with PowerTrade’s private endpoints, users must confirm their identity using JWT.
Depending on the service, the JWT must be included in the "X-Power-Trade" HTTP-header (REST API), ?power_trade HTTP-query, the login message (Session-based WebSocket protocol), or Password field (FIX protocol).
Please consult with the specific API documentation for more details.
1.1.1. Obtaining API keys and secrets
API keys can be obtained here https://app.power.trade/api-keys.
Other environments: WebApp/api-keys
1.1.2. Generating JWT
The following JWT claims are required in order to submit a valid credentials_secret:
-
client- Set toapi. -
uri- The production endpoint of the requested service. -
nonce- Monotonically increasing integer number. Usually, a timestamp can be used. -
iat- The Unix time at which the JWT was issued, in seconds. -
exp- The expiration Unix time on and after which the JWT must not be accepted for processing, in seconds. Must be less than iat+30sec. -
sub- The API Key.
The JWT should be signed with the API secret_key using the ES256 (elliptic curves using sha256) algorithm.
Please ensure the JWT is valid when sending the request.
PowerTrade will validate that current time is between iat and exp.
It is recommended to set the iat field to the current time minus 5 seconds and the exp field to the current time plus 30 seconds.
Examples
To go through the next few code samples, export the following environment variables in your shell.
# Replace with your api_key
export API_KEY="8557379d6d62080a1169740f183f16bf"
# Replace with your secret_key
export PEM_SECRET_KEY="-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILwnCHltSNt5BT+oB2C/I/YjI6OObYMaGLw0cTtOVHsroAoGCCqGSM49
AwEHoUQDQgAELzRQAq3U6JtDa7hLHTzX+tzlJurj1v2hcrHSdk4X3hzHQYJu1DB6
/gnZqe5mv3KS/HGvGCCyL1WFAz1S7VJ9uw==
-----END EC PRIVATE KEY-----"
TODO: place c++ code here
import os
import time
import jwt # Using PyJWT to generate JWT's - `pip install pyjwt`
def generate_credential_secret(api_key, pem_secret_key):
now = int(time.time())
payload = {
"client": "api",
"uri": "", # TODO - fill in with example production url
"nonce": now - 5,
"iat": now - 5,
"exp": now + 30,
"sub": api_key
}
try:
return jwt.encode(payload, pem_secret_key, algorithm="ES256")
except jwt.exceptions.PyJWTError as err:
print("Error: {}".format(err))
exit(0)
if __name__ == '__main__':
API_KEY = os.getenv("API_KEY")
PEM_SECRET_KEY = os.getenv("PEM_SECRET_KEY")
print(generate_credential_secret(API_KEY, PEM_SECRET_KEY))
package main
import (
"fmt"
"os"
"time"
"github.com/golang-jwt/jwt/v4"
)
type ClientCredentialsClaim struct {
Uri string
Client string
Nonce *jwt.NumericDate
jwt.RegisteredClaims
}
func GenerateCredentialsSecret() string {
apiKey := os.Getenv("API_KEY")
secretKey := os.Getenv("PEM_SECRET_KEY")
now := time.Now()
claims := ClientCredentialsClaim{
Uri: "", // TODO - fill in with example production url
Client: "api",
Nonce: jwt.NewNumericDate(now.UTC().Add(-5 * time.Second)),
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.UTC().Add(30 * time.Second)),
IssuedAt: jwt.NewNumericDate(now.UTC().Add(-5 * time.Second)),
Subject: apiKey,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
privateKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(secretKey))
if err != nil {
panic(err)
}
credentialsSecret, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}
return credentialsSecret
}
func main() {
fmt.Println(GenerateCredentialsSecret())
}
Example Ouput
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQiOiJhcGkiLCJub25jZSI6MTYxMTQwMjU0MSwiZXhwIjoxNjIwMDQyNTQxLCJpYXQiOjE2MTE0MDI1NDEsImlzcyI6InB0LXRlc3QuY2xlYXJwb29sLmRldjo0OTYyMSIsInN1YiI6Ijg1NTczNzlkNmQ2MjA4MGExMTY5NzQwZjE4M2YxNmJmIn0.oEjCgumgFfnYWw-otLW7vRAZ7p1KakMnDkmUz_LXWXKJlpZ3N7Ev6xeHSNEA9ZKJXwfHfEnvCAJbHWackjGSLA
2. Hosts
| Environment | REST | WS | WebApp |
|---|---|---|---|
DEV |
wss://api.wss.dev.power.trade/ |
||
TEST |
wss://api.wss.test.power.trade/ |
||
PROD |
wss://api.wss.prod.power.trade/ |
3. Public API Endpoints
Unix command line utilities used in the examples:
3.1. GET /v1/market_data/currency/:id/summary
Returns summary statistics relating to id currency.
id has two acceptable formats:
-
currency symbols (
BTC,ETH) -
numeric
deliverable_exchange_tokenids (3,4)
Return values:
volume - 24h volume in USD
price_change - 24h price change percentage
low_24 - 24h low price
high_24 - 24h high price
open_interest - total number of open contracts with this currency as the underlying asset
3.1.1. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/market_data/currency/BTC/summary
3.1.2. Sample Response
HTTP/1.1 200 OK
Content-Length: 125
Content-Type: application/json; charset=utf-8
Date: Mon, 16 Nov 2020 17:27:24 GMT
{
"volume": "5803847.256",
"volume_option": "4003847.256",
"volume_future": "0",
"volume_perpetual": "1800000",
"volume_spot": "0",
"price_change": "-3.570443868289867",
"low_24": "54768.5",
"high_24": "58963.32",
"open_interest": "25.6",
"index_price": "72984.35"
}
3.2. GET /v1/market_data/currency/all/summary
Returns summary statistics relating to all exchange currencies.
Return values:
id - deliverable_id
symbol - human-readable identifier for the deliverable
volume - 24h volume in USD
price_change - 24h price change percentage
low_24 - 24h low price
high_24 - 24h high price
open_interest - total number of open contracts with this currency as the underlying asset
3.2.1. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/market_data/currency/all/summary
3.2.2. Sample Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Mon, 16 Nov 2020 17:27:24 GMT
[
{
"symbol": "BTC",
"id": 3,
"volume": "5803847.256",
"volume_option": "4003847.256",
"volume_future": "0",
"volume_perpetual": "1800000",
"volume_spot": "0",
"price_change": "-0.9931941866406964",
"low_24": "32028.1",
"high_24": "32894.97",
"open_interest": "5.8",
"index_price": "72984.35"
},
{
"symbol": "ETH",
"id": 4,
"volume": "0",
"volume_option": "0",
"volume_future": "0",
"volume_perpetual": "0",
"volume_spot": "0",
"price_change": "-0.8983262503763592",
"low_24": "1994.4",
"high_24": "2093.15",
"open_interest": "0.5",
"index_price": "3098"
}
]
3.3. GET /v1/market_data/tradeable_entity/:id/summary
Returns summary statistics relating to id tradeable_entity.
id only has one acceptable format:
- numeric tradeable_entity ids (360,1034)
Return values:
symbol - human-readable identifier for the instrument
base_volume - 24h trade quantity in base currency
volume - 24h notional volume in quote currency (USD)
price_change - 24h price change percentage
low_24 - 24h low price
high_24 - 24h high price
last_price - last traded price
best_bid - the highest bid in the order book
best_ask - the lowest ask in the order book
open_interest - number of open contracts
index_price - index price of underlying asset in USD
3.3.1. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/market_data/tradeable_entity/360/summary
3.3.2. Sample Response
HTTP/1.1 200 OK
Content-Length: 125
Content-Type: application/json; charset=utf-8
Date: Mon, 16 Nov 2020 17:27:24 GMT
{
"symbol": "BTC-USD-PERPETUAL",
"volume": "848.6715",
"price_change": "0",
"low_24": "16803.7",
"high_24": "16803.7",
"last_price": "16803.7",
"open_interest": "7.7233",
"best_bid": "16803.7",
"best_ask": "16973.4",
"index_price": "16973.43",
"product_type": "perpetual_future"
}
3.4. GET /v1/market_data/tradeable_entity/all/summary
Returns summary statistics relating to all listed tradeable_entities.
Return values:
id - tradeable_entity_id
symbol - human-readable identifier for the instrument
volume - 24h notional volume in USD
price_change - 24h price change percentage
low_24 - 24h low price
high_24 - 24h high price
last_price - last traded price
best_bid - the highest bid in the order book
best_ask - the lowest ask in the order book
open_interest - number of open contracts
index_price - index price of underlying asset in USD
product_type - product type: `["spot", "index", "option", "future", "perpetual_future"]
3.4.1. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/market_data/tradeable_entity/all/summary
3.4.2. Sample Response
content-type: application/json; charset=utf-8
date: Fri, 23 Jul 2021 14:36:04 GMT
transfer-encoding: chunked
[
{
"id": 4,
"symbol": "BTC-USD",
"volume": "0",
"price_change": "0",
"low_24": "16548",
"high_24": "16548",
"last_price": "16548",
"open_interest": "0",
"best_bid": "16803.7",
"best_ask": "16973.4",
"index_price": "16973.43",
"product_type": "spot"
},
{
"id": 2,
"symbol": "ETH-USD-INDEX",
"volume": "0",
"price_change": "-0.22826873672741",
"low_24": "1246.14",
"high_24": "1261.19",
"last_price": null,
"open_interest": "0",
"best_bid": null,
"best_ask": null,
"index_price": "1254.42",
"product_type": "index"
},
{
"id": 622,
"symbol": "ETH-20221230-2200P",
"volume": "0",
"price_change": "0",
"low_24": "719.5",
"high_24": "719.5",
"last_price": "719.5",
"open_interest": "0",
"best_bid": null,
"best_ask": null,
"index_price": "1254.42",
"product_type": "option"
},
{
"id": 3747,
"symbol": "BTC-20230929",
"volume": "16973.43",
"price_change": "0.99795599374775",
"low_24": "16634",
"high_24": "16800",
"last_price": "16800",
"open_interest": "0.5",
"best_bid": "16634",
"best_ask": "16800",
"index_price": "16973.43",
"product_type": "future"
},
{
"id": 13,
"symbol": "BTC-USD-PERPETUAL",
"volume": "848.6715",
"price_change": "0",
"low_24": "16803.7",
"high_24": "16803.7",
"last_price": "16803.7",
"open_interest": "7.7233",
"best_bid": "16803.7",
"best_ask": "16973.4",
"index_price": "16973.43",
"product_type": "perpetual_future"
},
...
]
3.5. GET /v1/history/trades
Returns trades in time interval
3.5.1. Params
tradeable_entity_id - integer - [mandatory] filter by tradeable_entity_id
start_time - unix timestamp in nanoseconds - [optional] minimal time of trade [including]
end_time - unix timestamp in nanoseconds - [optional] maximal time of trade [including]
limit - integer - [optional] return not more than limit records
3.5.2. Sample Request
curl -X GET 'https://api.rest.dev.power.trade/v1/market_data/trades?tradeable_entity_id=4&limit=5'
3.5.3. Sample Response
[
{
"trade_id": "741540",
"timestamp": "1690975211306529000",
"side": "buy",
"price": "21150",
"quantity": "0.15"
},
{
"trade_id": "741539",
"timestamp": "1690974074765105000",
"side": "sell",
"price": "22150",
"quantity": "0.08"
},
{
"trade_id": "741538",
"timestamp": "1690974068717909000",
"side": "sell",
"price": "22150",
"quantity": "0.08"
},
{
"trade_id": "741537",
"timestamp": "1690973483635121000",
"side": "sell",
"price": "22150",
"quantity": "0.08"
},
{
"trade_id": "741536",
"timestamp": "1690973477585902000",
"side": "sell",
"price": "22150",
"quantity": "0.08"
}
]
4. Private API Endpoints
4.1. GET /v1/position/funds
Returns a snapshot of account’s balances of fundable currencies.
Immediately after account creation this snapshot is empty. New balances get added to snapshot after 1st balance change of the corresponding currency (deposit, trade, fee, etc).
4.1.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
4.1.2. Params
None
4.1.3. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/position/funds -H X-Power-Trade:${PT_ACCESS_TOKEN}
4.1.4. Sample Response
{
"balances": [
{
"currency": "SOL",
"amount": "100500",
"withdrawableBalance": "100500",
"availableBalance": "100500"
},
{
"currency": "ETH",
"amount": "2.3",
"withdrawableBalance": "2.3",
"availableBalance": "2.3016444005"
},
{
"currency": "BTC",
"amount": "12.9003",
"withdrawableBalance": "12.9003",
"availableBalance": "13.1341550363"
},
{
"currency": "USD",
"amount": "187628.6369145976",
"withdrawableBalance": "169704.5999345976",
"availableBalance": "169704.5999345976"
}
],
"clientAccountId": "236",
"accountHealth": "99.2948",
"accountRiskStatus": "normal"
}
4.2. GET /v1/position/holdings
Returns a snapshot of account’s holdings as currently holded derivatives.
4.2.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
4.2.2. Params
None
4.2.3. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/position/holdings -H X-Power-Trade:${PT_ACCESS_TOKEN}
4.2.4. Sample Response
{
"positions": [
{
"symbol": "ETH-USD-PERPETUAL",
"updateTimestamp": 1691142743121786000,
"amount": "-0.011",
"markPrice": "1837.19",
"indexPrice": "1841.69",
"upnl": "1.79091",
"marginValue": "3.0284595",
"avgEntryPrice": "2000",
"type": "perpetual_future"
},
{
"symbol": "BTC-USD-PERPETUAL",
"updateTimestamp": 1691142743121786000,
"amount": "-1.45",
"markPrice": "29190.72",
"indexPrice": "27129.79",
"upnl": "-17926.669",
"marginValue": "6344.4402",
"avgEntryPrice": "16827.5",
"type": "perpetual_future"
}
],
"clientAccountId": "236"
}
4.3. GET /v1/balance/funds_holdings
Returns a snapshot of all account’s assets: funds and currently holded derivatives.
4.3.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
4.3.2. Params
None
4.3.3. Sample Request
curl -X GET https://api.rest.dev.power.trade/v1/balance/funds_holdings -H X-Power-Trade:${PT_ACCESS_TOKEN}
4.3.4. Sample Response
[
{
"deliverable_id": "2",
"symbol": "USD",
"value": "187635.7433138798"
},
{
"deliverable_id": "3",
"symbol": "BTC",
"value": "12.9003"
},
{
"deliverable_id": "4",
"symbol": "ETH",
"value": "2.3"
},
{
"deliverable_id": "24",
"symbol": "BTC-USD-PERPETUAL",
"value": "-1.45"
},
{
"deliverable_id": "495",
"symbol": "ETH-USD-PERPETUAL",
"value": "-0.011"
},
{
"deliverable_id": "940",
"symbol": "SOL",
"value": "100500"
}
]
4.4. GET /v1/reporting/balance_activity
Retrieves the user balance activity
4.4.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
4.4.2. Params: all URI-based, no JSON payload in request, all optional, case-sensitive:
-
deliverable_id- ID of the deliverable for which to query balance activity:-
2: for USD -
3: for BTC -
4: for ETH -
N/A : returns balance activity for all deliverables;
-
-
market_id- ID of the trade market, default is any; -
update_reason- type of balance activity, case-sensitive:-
deposit -
withdrawal -
trade -
settlement -
funding -
N/A : querying activity of all types;
-
-
limit- max number of records to return; if not given, thendefault value = 50will be applied; -
start_time- start time (including) of the range query in Unix nanoseconds; no value or0result in no start_time constraint -
end_time- end time (including) of the range query in Unix nanoseconds; no value or0result in no end_time constraint -
next_sequence_number- [optional] pass from previous response to load next page
4.4.3. Sample Request
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?deliverable_id=2'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?start_time=1610309967000000000&next_sequence_number=151622'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?end_time=1610309967000000000'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?start_time=1610309967000000000&end_time=1620309967000000000'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?deliverable_id=2&update_reason=trade&limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?update_reason=trade&limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?update_reason=deposit&limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?update_reason=withdrawal&limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?update_reason=settlement&limit=10'
curl -X GET -H X-Power-Trade:${PT_ACCESS_TOKEN} 'https://api.rest.dev.power.trade/v1/reporting/balance_activity?update_reason=funding&limit=10'
4.4.4. Sample Response
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: application/json; charset=utf-8
Date: ...
in normal case:
{
"status": "OK",
"valid_at":"1234567890000000",
"activity_size": N,
"activity": [
{balance_change N: the most recent one},
{balance_change N-1},
{balance_change N-2},
...
{balance_change [0]: the oldest one},
]
}
in case of empty response:
{
"status": "OK",
"valid_at":"1234567890000000",
"activity_size": 0, // zero items in "activity"
"activity": [] // empty array returned
}
in case of error:
{
"status":"error", // Not Good
"message": "not exist in DB"
}
if balance activity count > limit:
{
"status": "OK",
"valid_at":"1234567890000000",
"activity_size": N,
"activity": [
{balance_change N: the most recent one},
{balance_change N-1},
{balance_change N-2},
...
{balance_change [0]: the oldest one},
],
"next_sequence_number": 12514422,
}
4.4.5. Examples of error messages ("message" values):
-
"unknown [parameter name]": check the name of parameter given, e.g."…Update_reason=deposit&…"; -
"incorrect value of [parameter name]": incorrect value of given parameter, e.g."…update_reason=Deposit&…"; -
"no value given for [parameter name]": no value given to parameter, e.g."…update_reason=&…"; -
"unable to query DB": issues with DB access;
The format of "activity" objects (please note that the "details" field is a JSON object whose content depends on "update_reason" value):
{
"update_reason": "deposit", // "withdrawal", "trade", "settlement", "funding"
"update_time": "1614145638558532000", // UTC timestamp of balance update
"deliverable_symbol": "USD", // symbol of corresponding deliverable which amount has changed
"amount": "1.0", // delta of balance change
"resulting_amount": "1.0", // resulting balance after this event
"details": { // provided as corresponding to "update_reason" value - one of the JSON objects below:
"deposit_details": {..}
or
"withdrawal_details": {..}
or
"trade_details": {..}
or
"settlement_details": {..} - for options
or
"settlement_details_future": {..} - for futures
or
"funding_details_perpetual": {..} - for perps
}
}
Please note the 3 fields above--"update_time", "amount", "resulting_amount"--define the characteristics of balance changes together with the "details" object.
4.4.6. Examples of "details" for different types of balance change:
deposit_details:
{
"asset_name": "USD",
"network_name": "ETH", // From supported asset's `nativeAsset`
"asset_id": "USDC",
"status": "Settled", // enum: "New","Rejected","Pending","Approved","Denied","Settled",
"transaction_type": "deposit", // enum: "deposit", "deposit-transfer", "deposit-test", "deposit-correction", "deposit-airdrop"
"transaction_id": "1",
"transaction_hash": "0efd3352bc63b7107b00dbe61280e03db5651…",
"note": "some optional note about the deposit"
}
withdraw_details:
{
"asset_name": "USD",
"network_name": "ETH", // From supported asset's `nativeAsset`
"asset_id": "USDC",
"status": "Settled", // enum: "New","Rejected","Pending","Approved","Denied","Settled",
"network_fee": "3",
"withdraw_fee": "3",
"transaction_type": "withdrawal", // enum: "withdrawal", "withdrawal-transfer", "withdrawal-correction"
"sent_to": "1EAWfLGSaZUrAKeYLoKHPRLpmMdHwQGcUf", // when type == "withdrawal" : sent_to = external blockchain address;
// when type == "withdrawal-transfer" : sent_to = account_id;
// when type == "withdrawal-correction" : sent_to is empty string "";
"transaction_id": "3",
"transaction_hash": "0adb9f28d2377b56f58244663352bc63b7107b00dbe61280e03db5651…"
"note": "some optional note about the withdrawal"
}
trade_details:
{
"symbol": "BTC-20210305-100000C",
"market_id": "0",
"product_type": "option", // can be "future", "perpetual_future", "index", "fx_spot", "currency"
"trade_id": "123456677890",
"order_side": "self",
"order_id": "1234567890",
"order_reference": "a1a1674812937440a00000000000000000000000000000000000000000000000",
"liquidity_flag": "taken", // or "added"
"trade_source": "party", // or "liquidation"
"quantity": "0.4",
"limit_price": "1625.34",
"fee_input_price": "66468.25", // index price for derivatives, trade price for spot
"trade_fee": "-0.3900816", // can be omitted if doesn't exist
"trade_credit_fee": "-0.1", // can be omitted if doesn't exist
"trade_tax": "-0.12", // can be omitted if doesn't exist
"trade_credit_tax": "-0.11", // can be omitted if doesn't exist
"trade_fee_deliverable_id": "2",
"trade_fee_symbol": "USD",
"price_result_cash": "2005.34", // Present only if requested deliverable is quantity deliverable
"creation_time": "2024-07-30T05:28:02.986248Z",
"settlement_symbol": "USD",
"contract_size_symbol": "BTC",
"expires_at":{
"datetime":{
"date": {
"year":"2021",
"month":"March",
"day":"5"},
"time":{
"hours":"08",
"minutes":"00",
"seconds":"00",
"nanoseconds":"000000000"
}
}
},
"option_type": "call", // or "put", optional field - only for "product_type"=="option"
"strike_price": "100000"
}
trade_details (self-trade):
{
"update_reason": "trade",
"update_time": "2023-01-27T09:48:57.451911Z",
"deliverable_symbol": "USD",
"amount": "0",
"resulting_amount": "105974.5328763398",
"details": {
"trade_details": {
"symbol": "ETH-USD",
"market_id": "0",
"product_type": "spot",
"trade_id": "711582",
"order_side": "self",
"order_id": "12938099",
"order_reference": "a1a1674812937440a00000000000000000000000000000000000000000000000",
"liquidity_flag": "taken",
"trade_source": "party",
// Additional fields for sell-side of self-trade
"sell_order_id": "12938097",
"sell_order_reference": "a1a1674812932152a00000000000000000000000000000000000000000000000",
"sell_liquidity_flag": "added",
"sell_trade_source": "party",
"quantity": "0.4",
"limit_price": "1625.34",
"fee_input_price": "66468.25",
"trade_fee": "-0.3900816",
"trade_fee_deliverable_id": "2",
"trade_fee_symbol": "USD",
"creation_time": "2024-07-30T05:28:02.986248Z",
"quantity_deliverable_symbol": "ETH",
"price_deliverable_symbol": "USD"
}
}
}
trade_details (direct trade):
{
"update_reason": "trade",
"update_time": "2026-01-08T07:30:56.704422Z",
"deliverable_symbol": "BTC",
"amount": "-0.03",
"resulting_amount": "0.12",
"resulting_credit": "0",
"details": {
"trade_details": {
"symbol": "BTC-VND",
"market_id": "2",
"product_type": "spot",
"trade_id": "221",
"order_side": "sell",
"order_id": "211152844",
"order_reference": "a1a1767857456633a00000000000000000000000000000000000000000000000",
"liquidity_flag": "added",
"trade_source": "party",
"quantity": "0.03",
"limit_price": "2438910000",
"fee_input_price": "2438910000",
"trade_fee": "-73168",
"trade_tax": "-73167",
"trade_fee_deliverable_id": "15",
"trade_fee_symbol": "VND",
"price_result_cash": "713540844",
"creation_time": "2026-01-08T07:30:56.678446Z",
"opposite_display_account_id": "29",
"opposite_username": "Magic Lynx", // if user has set its `username`
"quantity_deliverable_symbol": "BTC",
"price_deliverable_symbol": "VND"
}
}
}
settlement_details (for options):
{
"settlement_event_type": "expiry",
"symbol":"BTC-20210226-100000C",
"product_type": "option",
"position_side": "long",
"option_type": "call",
"strike_price": "360000.0",
"settlement_symbol": "USD",
"contract_size_symbol": "BTC",
"quantity": "300.0",
"settlement_price": "300.0",
"settlement_fee": "-0.006789",
"settlement_credit_fee": "-0.1", // can be omitted if doesn't exist
"settlement_id": "1234567890",
"settlement_event": "2021-03-17T01:24:40.130589Z",
"expires_at":{
"datetime":{
"date": {
"year":"2021",
"month":"March",
"day":"5"},
"time":{
"hours":"08",
"minutes":"00",
"seconds":"00",
"nanoseconds":"000000000"
}
}
},
}
settlement_details_future "cyclical" type of settlement:
{
"settlement_event_type": "cyclical",
"symbol":"BTC-20210226",
"product_type": "future",
"position_side": "long",
"settlement_symbol": "USD",
"contract_size_symbol": "BTC",
"quantity": "300.0",
"settlement_price": "300.0",
"settlement_id": "1234567890",
"settlement_event": "2021-03-17T01:24:40.130589Z",
"expires_at":{
"datetime":{
"date": {
"year":"2021",
"month":"March",
"day":"5"},
"time":{
"hours":"08",
"minutes":"00",
"seconds":"00",
"nanoseconds":"000000000"
}
}
},
}
settlement_details_future "expiry" type of settlement:
{
"settlement_event_type": "expiry",
"symbol":"BTC-20210226",
"product_type": "future",
"position_side": "long",
"settlement_symbol": "USD",
"contract_size_symbol": "BTC",
"quantity": "300.0",
"settlement_price": "300.0",
"settlement_fee": "-0.006789",
"settlement_credit_fee": "-0.1", // can be omitted if doesn't exist
"settlement_id": "1234567890",
"settlement_event": "2021-03-17T01:24:40.130589Z",
"expires_at":{
"datetime":{
"date": {
"year":"2021",
"month":"March",
"day":"5"},
"time":{
"hours":"08",
"minutes":"00",
"seconds":"00",
"nanoseconds":"000000000"
}
}
},
}
funding_details_perpetual:
{
"settlement_event_type": "cyclical",
"symbol":"BTC-USD-PERPETUAL",
"product_type": "perpetual_future",
"position_side": "long",
"settlement_symbol": "USD",
"contract_size_symbol": "BTC",
"quantity": "300.0",
"index_price": "300.0",
"mark_price": "300.0",
"funding_rate": "0.0023",
"settlement_id": "1234567890",
"settlement_event": "2021-03-17T01:24:40.130589Z",
}
correction/correction_fee event:
This message is used to, for example: - repay credit if credit was used to cover a trade fee; - adjust settlement in case of incorrect expiration price;
Fields:
- credit_delta - is the change of credit in the correction;
- total_cash_delta, total_credit_delta, total_cash_fee_delta, total_credit_fee_delta - are the sum of all deltas of the event (e.g. trade) including correction;
{
"update_reason": "correction_fee",
"update_time": "2025-07-19T04:49:37.604706Z",
"deliverable_symbol": "USD",
"amount": "-0.9358978836311574045032",
"resulting_amount": "115.5158255163688425954968",
"resulting_credit": "-0.98",
"details": {
"correction_details": {
"credit_delta": "0.9358978836311574045032",
"total_cash_delta": "-15693.0",
"total_credit_delta": "0.0",
"total_cash_fee_delta": "-1.882605",
"total_credit_fee_delta": "0.0",
"target_event": {
"trade_details": {
"symbol": "ETH-20250613-2480C",
"product_type": "option",
"trade_id": "11286063",
"order_side": "buy",
"order_id": "3386230383",
"order_reference": "a1a1749225798833a00000000000000000000000000000000000000000000000",
"liquidity_flag": "taken",
"trade_source": "party",
"quantity": "1",
"limit_price": "15693",
"fee_input_price": "2510.14",
"trade_fee": "-0.9467071163688425954968",
"trade_credit_fee": "-0.9358978836311574045032",
"trade_fee_deliverable_id": "2",
"creation_time": "2025-06-06T16:03:19.096369Z",
"settlement_symbol": "USD",
"contract_size_symbol": "ETH",
"expires_at": {
"datetime": {
"date": {
"year": "2025",
"month": "6",
"day": "13"
},
"time": {
"hours": "08",
"minutes": "00",
"seconds": "00",
"nanoseconds": "000000000"
}
}
},
"option_type": "call",
"strike_price": "2480.0"
}
}
}
}
}
generic event (in case of reconciliation etc):
{
"update_reason": "generic",
"update_time": "2022-10-14T01:25:51.971513Z",
"deliverable_symbol": "USD",
"amount": "-57.05",
"resulting_amount": "2001224.9167663585",
"details": {
"generic_details": {
"fee": "0",
"credit_amount": "-0.1", // can be omitted if doesn't exist
"credit_fee": "0.1", // can be omitted if doesn't exist
"trade_serial": null,
"funds_id": null,
"settlement_id": "49650"
}
}
}
5. MFA / Step-Up Authentication
Certain sensitive actions require step-up authentication (multi-factor authentication) before they can be completed. This follows RFC 9470 (OAuth 2.0 Step-Up Authentication Challenge Protocol).
5.1. Overview
When a protected endpoint is called without a valid MFA challenge, the server responds with 401 Unauthorized and a WWW-Authenticate header. The response body contains the challenge details (including challenge_id). The client must then:
-
Verify the required MFA methods against the challenge.
-
Retry the original request with the
X-Step-Up-Challengeheader set to the challenge ID.
5.2. Protected Actions
The following actions may require step-up authentication (configurable per deployment):
| Action | Endpoint | Description |
|---|---|---|
|
|
Create a withdrawal |
|
|
Add a withdrawal address |
|
|
Create an API key |
|
|
Disable two-factor authentication |
5.3. Verification Methods
| Method | Description |
|---|---|
|
Time-based one-time password (authenticator app). Available only if the user has enabled 2FA. |
|
One-time code sent to the user’s registered email address. Available for all users. |
5.4. Policy Modes
| Mode | Description |
|---|---|
|
Any one of the listed methods is sufficient to complete the challenge. |
|
All listed methods must be verified to complete the challenge. |
5.5. Step-Up Authentication Flow
-
Client calls a protected endpoint (e.g.
POST /v2/api_keys). -
If the user does not meet the method requirements (e.g.
mode: allrequires TOTP + email, but user has not enrolled TOTP), the server returns403 Forbiddenwith a structured error (see Method Requirements Not Met (403)). -
Otherwise, the server returns
401 Unauthorizedwith a challenge (see Step-Up Challenge Response (401)). -
Client verifies one or more methods via
POST /v2/mfa/verify(see POST /v2/mfa/verify). -
Client can check challenge status via
GET /v2/mfa/challenges/:id(see GET /v2/mfa/challenges/:id). -
Once the challenge is complete, client retries the original request with the
X-Step-Up-Challenge: <challenge_id>header. -
The challenge is consumed (single-use) and the original action proceeds.
5.6. Method Requirements Not Met (403)
When a protected endpoint requires MFA but the user has not enrolled all required verification methods, the server returns 403 Forbidden with a structured response indicating what is missing.
This occurs when the policy mode is all and the user has not enrolled every required method (e.g. TOTP not enabled), or when no methods are available at all.
5.6.1. Sample Response (403 Forbidden)
{
"status": "error",
"error": "mfa_methods_not_met",
"message": "MFA required but verification method requirements not met",
"required": ["totp", "email"],
"available": ["email"],
"missing": ["totp"],
"mode": "all"
}
| Field | Type | Description |
|---|---|---|
|
String |
Always |
|
String |
Error code: |
|
String |
Human-readable description |
|
String[] |
All methods the policy requires |
|
String[] |
Methods the user currently has enrolled |
|
String[] |
Methods the user needs to enroll (required minus available) |
|
String |
Policy mode: |
|
Note
|
The client should prompt the user to enroll the missing methods (e.g. enable TOTP via POST /v2/profile/2fa/enable) before retrying the action.
|
5.7. Step-Up Challenge Response (401)
When a protected endpoint requires MFA and no valid challenge is presented, the server returns:
5.7.1. Response Headers
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication", error_description="Step-up authentication required", acr_values="urn:nist:800-63b:aal2"
5.7.2. Response Body
{
"error": "insufficient_user_authentication",
"challenge_id": "550e8400-e29b-41d4-a716-446655440000",
"acr_values": "urn:nist:800-63b:aal2",
"methods": ["totp", "email"],
"mode": "any",
"expires_at": "2026-03-12T12:10:00Z",
"initiated": {
"email": true
}
}
| Field | Type | Description |
|---|---|---|
|
String |
RFC 9470 error code: |
|
String |
UUID of the created challenge; use this for verification and retry |
|
String |
NIST AAL level required (e.g. |
|
String[] |
Verification methods required for this challenge |
|
String |
Policy mode: |
|
String |
ISO 8601 timestamp when the challenge expires |
|
Object |
Map of method name to boolean; |
|
Note
|
When the initiated map shows "email": true, an OTP code has already been sent to the user’s email. TOTP never appears in initiated because the user generates it from their authenticator app.
|
5.8. POST /v2/mfa/verify
Verify a single method against a pending MFA challenge.
5.8.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
5.8.2. Request Body
{
"challenge_id": "550e8400-e29b-41d4-a716-446655440000",
"method": "totp",
"code": "123456"
}
| Field | Type | Required | Description |
|---|---|---|---|
|
String |
Yes |
UUID of the challenge to verify against |
|
String |
Yes |
Verification method: |
|
String |
Yes |
The one-time code (6 digits for TOTP; 6 digits for email OTP) |
5.8.3. Sample Request
curl -X POST https://api.rest.dev.power.trade/v2/mfa/verify \
-H "X-Power-Trade: ${PT_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"challenge_id":"550e8400-e29b-41d4-a716-446655440000","method":"totp","code":"123456"}'
5.8.4. Sample Response (200 OK)
{
"status": "ok",
"challenge_id": "550e8400-e29b-41d4-a716-446655440000",
"complete": true,
"consumed": false,
"acr_values": "urn:nist:800-63b:aal2",
"verified": ["totp"],
"remaining": []
}
| Field | Type | Description |
|---|---|---|
|
String |
|
|
String |
UUID of the challenge |
|
Boolean |
|
|
Boolean |
|
|
String |
NIST AAL level of the challenge |
|
String[] |
Methods that have been successfully verified so far |
|
String[] |
Methods still remaining to be verified (empty when |
5.8.5. Error Responses
{
"status": "error",
"message": "challenge expired"
}
Possible error messages:
-
"challenge not found"— invalid or non-existent challenge ID -
"challenge expired"— the challenge TTL has elapsed -
"challenge already consumed"— the challenge was already used -
"method not required"— the specified method is not part of this challenge -
"method already verified"— the method was already verified for this challenge -
"too many attempts"— maximum verification attempts exceeded (default: 5 per challenge) -
"invalid code"— the OTP code is incorrect
5.9. GET /v2/mfa/challenges/:id
Retrieve the current status of an MFA challenge. Only the user who owns the challenge can access it.
5.9.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
5.9.2. Params
-
:id— UUID of the challenge (path parameter)
5.9.3. Sample Request
curl -X GET https://api.rest.dev.power.trade/v2/mfa/challenges/550e8400-e29b-41d4-a716-446655440000 \
-H "X-Power-Trade: ${PT_ACCESS_TOKEN}"
5.9.4. Sample Response (200 OK)
{
"status": "ok",
"challenge_id": "550e8400-e29b-41d4-a716-446655440000",
"action": "api_key_create",
"acr_values": "urn:nist:800-63b:aal2",
"mode": "any",
"methods": ["totp", "email"],
"verified": ["totp"],
"remaining": [],
"complete": true,
"consumed": false,
"expires_at": "2026-03-12T12:10:00Z"
}
| Field | Type | Description |
|---|---|---|
|
String |
|
|
String |
UUID of the challenge |
|
String |
The protected action this challenge is for (e.g. |
|
String |
NIST AAL level required |
|
String |
Policy mode: |
|
String[] |
All verification methods required for this challenge |
|
String[] |
Methods successfully verified so far |
|
String[] |
Methods still needing verification |
|
Boolean |
Whether the challenge has been fully verified |
|
Boolean |
Whether the challenge has been used to authorize an action |
|
String |
ISO 8601 expiration timestamp |
5.9.5. Error Response
{
"status": "error",
"message": "challenge not found"
}
5.10. GET /v2/mfa/methods
List the MFA verification methods available for the current user.
5.10.1. Header
X-Power-Trade: {PT_ACCESS_TOKEN}
5.10.2. Params
None
5.10.3. Sample Request
curl -X GET https://api.rest.dev.power.trade/v2/mfa/methods \
-H "X-Power-Trade: ${PT_ACCESS_TOKEN}"
5.10.4. Sample Response (200 OK) — user with 2FA enabled
{
"status": "ok",
"methods": ["totp", "email"]
}
5.10.5. Sample Response (200 OK) — user without 2FA
{
"status": "ok",
"methods": ["email"]
}
| Field | Type | Description |
|---|---|---|
|
String |
|
|
String[] |
Available verification methods for this user |
5.11. Retrying a Protected Action with a Completed Challenge
Once a challenge is complete ("complete": true), include its ID in the X-Step-Up-Challenge header when retrying the original protected endpoint:
5.11.1. Sample Request
curl -X POST https://api.rest.dev.power.trade/v2/api_keys \
-H "X-Power-Trade: ${PT_ACCESS_TOKEN}" \
-H "X-Step-Up-Challenge: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"label":"my-new-key","permissions":["owner"]}'
The challenge is consumed after successful use and cannot be reused.
If the challenge is invalid, expired, or already consumed, the server returns 401 Unauthorized with an updated WWW-Authenticate header describing the error.
6. Revision History
| Date | Version | Notes |
|---|---|---|
2026-03-12 |
3.7 |
Add MFA / Step-Up Authentication endpoints (RFC 9470) |
2025-07-21 |
3.6 |
Add Balance Activity: |
2025-03-12 |
3.5 |
Improved |
2024-11-16 |
3.4 |
Market Data: Currency: add "index_price" and per-type volume |
2024-07-30 |
3.3 |
Balance activity: added "fee_input_price" for trades |
2024-02-23 |
3.2 |
Balance activity: improved reporting of self-trades |
2024-02-13 |
3.1 |
Add Hosts and Authentication sections |
2024-02-09 |
3.0 |
Public API endpoints |
2024-01-04 |
2.0 |
Reviewed edition for publishing |
2023-01-05 |
1.0 |
Initial version |