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 to api.

  • 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

https://api.rest.dev.power.trade/

wss://api.wss.dev.power.trade/

https://powertrade-web-dev.web.app/

TEST

https://api.rest.test.power.trade/

wss://api.wss.test.power.trade/

https://powertrade-web-test.web.app/

PROD

https://api.rest.prod.power.trade/

wss://api.wss.prod.power.trade/

https://app.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_token ids (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, then default value = 50 will be applied;

  • start_time - start time (including) of the range query in Unix nanoseconds; no value or 0 result in no start_time constraint

  • end_time - end time (including) of the range query in Unix nanoseconds; no value or 0 result 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:

  1. Verify the required MFA methods against the challenge.

  2. Retry the original request with the X-Step-Up-Challenge header set to the challenge ID.

5.2. Protected Actions

The following actions may require step-up authentication (configurable per deployment):

Action Endpoint Description

withdrawal_request

POST /v2/wallet/withdrawals

Create a withdrawal

withdrawal_address

POST /v2/wallet/withdrawal_addresses

Add a withdrawal address

api_key_create

POST /v2/api_keys

Create an API key

disable_2fa

POST /v2/profile/2fa/disable

Disable two-factor authentication

5.3. Verification Methods

Method Description

totp

Time-based one-time password (authenticator app). Available only if the user has enabled 2FA.

email

One-time code sent to the user’s registered email address. Available for all users.

5.4. Policy Modes

Mode Description

any

Any one of the listed methods is sufficient to complete the challenge.

all

All listed methods must be verified to complete the challenge.

5.5. Step-Up Authentication Flow

  1. Client calls a protected endpoint (e.g. POST /v2/api_keys).

  2. If the user does not meet the method requirements (e.g. mode: all requires TOTP + email, but user has not enrolled TOTP), the server returns 403 Forbidden with a structured error (see Method Requirements Not Met (403)).

  3. Otherwise, the server returns 401 Unauthorized with a challenge (see Step-Up Challenge Response (401)).

  4. Client verifies one or more methods via POST /v2/mfa/verify (see POST /v2/mfa/verify).

  5. Client can check challenge status via GET /v2/mfa/challenges/:id (see GET /v2/mfa/challenges/:id).

  6. Once the challenge is complete, client retries the original request with the X-Step-Up-Challenge: <challenge_id> header.

  7. 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

status

String

Always error

error

String

Error code: mfa_methods_not_met

message

String

Human-readable description

required

String[]

All methods the policy requires

available

String[]

Methods the user currently has enrolled

missing

String[]

Methods the user needs to enroll (required minus available)

mode

String

Policy mode: any or all

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

error

String

RFC 9470 error code: insufficient_user_authentication

challenge_id

String

UUID of the created challenge; use this for verification and retry

acr_values

String

NIST AAL level required (e.g. urn:nist:800-63b:aal2)

methods

String[]

Verification methods required for this challenge

mode

String

Policy mode: any or all (see Policy Modes above)

expires_at

String

ISO 8601 timestamp when the challenge expires

initiated

Object

Map of method name to boolean; true means the method was automatically initiated (e.g. email OTP was sent)

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

challenge_id

String

Yes

UUID of the challenge to verify against

method

String

Yes

Verification method: totp or email

code

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

status

String

ok on success

challenge_id

String

UUID of the challenge

complete

Boolean

true if enough methods have been verified per the policy mode

consumed

Boolean

true if the challenge has already been used to authorize an action (single-use)

acr_values

String

NIST AAL level of the challenge

verified

String[]

Methods that have been successfully verified so far

remaining

String[]

Methods still remaining to be verified (empty when complete is true in any mode; may still list other methods)

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

status

String

ok on success

challenge_id

String

UUID of the challenge

action

String

The protected action this challenge is for (e.g. api_key_create)

acr_values

String

NIST AAL level required

mode

String

Policy mode: any or all

methods

String[]

All verification methods required for this challenge

verified

String[]

Methods successfully verified so far

remaining

String[]

Methods still needing verification

complete

Boolean

Whether the challenge has been fully verified

consumed

Boolean

Whether the challenge has been used to authorize an action

expires_at

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

status

String

ok on success

methods

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: correction, correction_fee events

2025-03-12

3.5

Improved Authentication section

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