> For the complete documentation index, see [llms.txt](https://docs.kryptox.finance/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kryptox.finance/api/websocket-trading-enpoints.md).

# WebSocket Trading Enpoints

## Overview

1. This endpoint lets users place and cancel orders over WebSocket.
2. Connection URL: `wss://wsapi.kryptox.finance/ws-api/v1`
3. After the connection is established, the client must send a `logon` request to authenticate.
4. The connection may drop; clients should reconnect and authenticate again.

## Signature & Authentication

After establishing the WebSocket connection, the client must send a unified `logon` request within 20 seconds:

```json
{
  "id": "97979",
  "op": "logon",
  "args": {
    "user": "0x...",
    "nft": "",
    "signer": "0x...",
    "nonce": "1760000000000",
    "sign": "<eip712-signature-hex>"
  }
}
```

Field descriptions:

* `id`: client-defined request ID. It must be a non-empty string with a maximum length of 39 characters.
* `op`: fixed as `logon`.
* `args.user`: wallet address. In normal-wallet mode, this is also the address expected to be recovered by EIP-712 verification.
* `args.nft`: sub-account NFT id. Pass an empty string or omit it when not used.
* `args.signer`: optional proxy wallet address. When `signer` exists and differs from `user`, proxy-wallet mode is used, and the address expected to be recovered by EIP-712 verification is `signer`.
* `args.nonce`: millisecond timestamp string. By default, it may differ from the server's current time by at most 5000 ms.
* `args.sign`: EIP-712 signature, hex string.

Authentication success response:

```json
{
  "id": "97979",
  "code": "0",
  "data": "success"
}
```

### EIP-712 Signing String

In normal-wallet mode, the canonical string is fixed as:

```plaintext
user=<url_encode(user)>&nft=<url_encode(nft)>&nonce=<url_encode(nonce)>
```

In proxy-wallet mode (`signer` exists and differs from `user`), the canonical string is fixed as:

```plaintext
user=<url_encode(user)>&nft=<url_encode(nft)>&signer=<url_encode(signer)>&nonce=<url_encode(nonce)>
```

Notes:

* The parameter order is fixed and must not be changed.
* Every field is URL-encoded.
* `nonce` is a millisecond timestamp string.
* Normal-wallet mode signs with the private key of `user`; proxy-wallet mode signs with the private key of `signer`.
* The signing string does not include connection-level session information.

Default EIP-712 domain configuration:

```plaintext
name: kryptox
version: 1
chainId: 1666
verifyingContract: 0x0000000000000000000000000000000000000000
```

## WebSocket Requests

The JSON body of a business request has the following structure:

```json
{
  "id": "44324113299",
  "op": "order",
  "args": {}
}
```

Notes:

* `id`: client-defined request ID. It must be a non-empty string with a maximum length of 39 characters.
* `op`: operation name. The futures side currently supports `order`, `cancel`, `batch_order`, and `batch_cancel`.
* `args`: business parameters, aligned with the corresponding REST/OpenAPI parameters.

### ping

To prevent the server from closing the TCP connection, the client may periodically send ping messages to keep the connection alive:

```json
{
  "id": "1545910660739",
  "op": "ping"
}
```

Server response:

```json
{
  "id": "1545910660739",
  "code": "0",
  "data": {
    "op": "pong",
    "timestamp": 1780755553813750
  }
}
```

Notes:

* `id` only supports strings.
* A ping request does not need `args` or `timestamp`.
* `data.timestamp` in the pong response is the server timestamp in microseconds.
* WebSocket protocol-level ping frames are also valid.

## Response Format

Normal successful business response:

```json
{
  "id": "ws-order-1779094055598",
  "code": "0",
  "data": {
    "orderId": "445774214062931968",
    "clientOid": "demo-ws-order-1779094055"
  },
  "rateLimit": {
    "limit": 1600,
    "reset": 15244,
    "remaining": 1528
  }
}
```

Error response:

```json
{
  "id": "ws-order-1779094055598",
  "code": "40702",
  "msg": "Too many requests in a short period of time, please retry later.",
  "rateLimit": {
    "limit": 1600,
    "reset": 15244,
    "remaining": 1528
  }
}
```

## Place Order

Request format:

```json
{
  "id": "ws-order-1779094055598",
  "op": "order",
  "args": {
    "symbol": "BTCUSDC",
    "type": "limit",
    "side": "buy",
    "size": "0.001",
    "price": "50000",
    "clientOid": "demo-ws-order-1779094055"
  }
}
```

Response:

```json
{
  "id": "ws-order-1779094055598",
  "code": "0",
  "data": {
    "orderId": "445774214062931968",
    "clientOid": "demo-ws-order-1779094055"
  }
}
```

## Cancel Order

Cancel by `clientOid` or `orderId`.Request format:

```json
{
  "id": "ws-cancel-1779177753388",
  "op": "cancel",
  "args": {
    "symbol": "BTCUSDC",
    "clientOid": "demo-ws-order-1779177753",
    "orderId": "446131955151405056"
  }
}
```

Notes:

* When both `clientOid` and `orderId` are provided, `orderId` takes priority.
* `symbol` is required.

Response:

```json
{
  "id": "ws-cancel-1779260770008",
  "code": "0",
  "data": {
    "clientOid": "demo-ws-order-1779260753"
  }
}
```

## Batch Place Orders

Request format:

```json
{
  "id": "ws-batch-order-1779260095011",
  "op": "batch_order",
  "args": [
    {
      "symbol": "BTCUSDC",
      "type": "limit",
      "side": "buy",
      "size": "0.001",
      "price": "50000",
      "clientOid": "demo-ws-order-a"
    },
    {
      "symbol": "BTCUSDC",
      "type": "limit",
      "side": "buy",
      "size": "0.001",
      "price": "51000",
      "clientOid": "demo-ws-order-b"
    }
  ]
}
```

Notes:

* The `args` parameters are aligned with the REST batch place-order endpoint.

## Batch Cancel Orders

Request format:

```json
{
  "id": "ws-batch-cancel-1779260095011",
  "op": "batch_cancel",
  "args": {
    "clientOidsList": [
      {
        "symbol": "BTCUSDC",
        "clientOid": "demo-ws-order-1779259996"
      },
      {
        "symbol": "BTCUSDC",
        "clientOid": "demo-ws-order-1779260075"
      }
    ]
  }
}
```

You can also cancel in batch by `orderId`:

```json
{
  "id": "ws-batch-cancel-1779260095012",
  "op": "batch_cancel",
  "args": {
    "orderIdsList": [
      {
        "symbol": "BTCUSDC",
        "orderId": "445774214062931968"
      },
      {
        "symbol": "BTCUSDC",
        "orderId": "445774214062931969"
      }
    ]
  }
}
```

Notes:

* Both `orderIdsList` and `clientOidsList` are lists of objects, and each item must include `symbol`.

Response:

```json
{
  "id": "ws-batch-cancel-1779260095011",
  "code": "0",
  "data": [
    {
      "orderId": null,
      "clientOid": "demo-ws-order-1779259996",
      "code": "0",
      "msg": "success"
    },
    {
      "orderId": null,
      "clientOid": "demo-ws-order-1779260075",
      "code": "0",
      "msg": "success"
    }
  ]
}
```

## Code Example

The following example shows the key flow for authentication, order placement, and heartbeat.

```python
#!/usr/bin/env python3
import json
import time
import urllib.parse

import websocket
from eth_account import Account
from eth_account.messages import encode_typed_data


WS_URL = "wss://wsapi.kryptox.finance/ws-api/v1"
USER = "YOUR-WALLET-ADDRESS"
PRIVATE_KEY = "YOUR-WALLET-PRIVATE-KEY"
NFT = ""

TYPED_DATA_TEMPLATE = {
    "types": {
        "EIP712Domain": [
            {"name": "name", "type": "string"},
            {"name": "version", "type": "string"},
            {"name": "chainId", "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        "Message": [{"name": "msg", "type": "string"}],
    },
    "primaryType": "Message",
    "domain": {
        "name": "kryptox",
        "version": "1",
        "chainId": 1666,
        "verifyingContract": "0x0000000000000000000000000000000000000000",
    },
    "message": {"msg": ""},
}


def json_dumps(data):
    return json.dumps(data, separators=(",", ":"))


def sign_eip712_message(message_text, private_key):
    typed_data = json.loads(json.dumps(TYPED_DATA_TEMPLATE))
    typed_data["message"]["msg"] = message_text
    message = encode_typed_data(full_message=typed_data)
    return Account.sign_message(message, private_key=private_key).signature.hex()


def build_logon_args(user, nft, private_key):
    nonce = str(int(time.time() * 1000))
    values = {
        "user": user,
        "nft": nft,
        "nonce": nonce,
    }
    canonical = "&".join(
        f"{key}={urllib.parse.quote(str(values[key]), safe='')}"
        for key in ("user", "nft", "nonce")
    )
    return {
        "user": user,
        "nft": nft or "",
        "nonce": nonce,
        "sign": sign_eip712_message(canonical, private_key),
    }


ws = websocket.create_connection(WS_URL)

logon = {
    "id": "logon-1",
    "op": "logon",
    "args": build_logon_args(USER, NFT, PRIVATE_KEY),
}
ws.send(json_dumps(logon))
print(ws.recv())

order = {
    "id": "ws-order-1",
    "op": "order",
    "args": {
        "symbol": "BTCUSDC",
        "type": "limit",
        "side": "buy",
        "size": "0.001",
        "price": "50000",
        "clientOid": "demo-ws-order-1",
    },
}
ws.send(json_dumps(order))
print(ws.recv())

ping = {
    "id": "ping-1",
    "op": "ping",
}
ws.send(json_dumps(ping))
print(ws.recv())

ws.close()
```

## Rate Limiting

ws-api and REST share the API resource pool. When the resource pool reaches 0, requests are rate-limited.Response example:

```json
{
  "id": "ws-order-1779094055598",
  "code": "40702",
  "msg": "Too many requests in a short period of time, please retry later.",
  "rateLimit": {
    "limit": 1600,
    "reset": 15244,
    "remaining": 1528
  }
}
```

| Field       | Meaning                                     |
| ----------- | ------------------------------------------- |
| `limit`     | resource pool size                          |
| `reset`     | remaining time until reset, in milliseconds |
| `remaining` | remaining resource pool size                |

## Common Error Codes

<table data-header-hidden><thead><tr><th width="155.2578125"></th><th></th></tr></thead><tbody><tr><td>code</td><td>msg</td></tr><tr><td><code>10701</code></td><td><code>Please check the url of your request:</code></td></tr><tr><td><code>10702</code></td><td><code>Invalid nonce.</code></td></tr><tr><td><code>10703</code></td><td><code>Invalid request data:</code></td></tr><tr><td><code>10704</code></td><td><code>Please check the param of your request:</code></td></tr><tr><td><code>10705</code></td><td><code>Receive data error.</code></td></tr><tr><td><code>20002</code></td><td><code>Authorization is required.</code></td></tr><tr><td><code>20005</code></td><td><code>Url in user blacklist.</code></td></tr><tr><td><code>20703</code></td><td><code>Invalid signature.</code></td></tr><tr><td><code>20704</code></td><td><code>Invalid request ip, the current clientIp is: %s</code></td></tr><tr><td><code>20705</code></td><td><code>Access denied, require more permission</code></td></tr><tr><td><code>20707</code></td><td><code>Uid Access denied, require more permission</code></td></tr><tr><td><code>20708</code></td><td><code>Session authentication failed.</code></td></tr><tr><td><code>20709</code></td><td><code>Session authentication has timed out.</code></td></tr><tr><td><code>20710</code></td><td><code>Account function temporarily restricted for security reasons. Contact client services for support.</code></td></tr><tr><td><code>20711</code></td><td><code>Account function temporarily restricted for security reasons. Contact client services for support.</code></td></tr><tr><td><code>20714</code></td><td><code>Invalid request ip.</code></td></tr><tr><td><code>20715</code></td><td><code>This operation cannot be completed at this time due to local laws, regulations, or policies in the country or region in which you reside.</code></td></tr><tr><td><code>20716</code></td><td><code>Based on your IP address, we currently do not provide services in your country or region due to local laws, regulations, or policies. We apologize for any inconvenience this may cause. If you have any questions, please contact customer service.</code></td></tr><tr><td><code>20717</code></td><td><code>To enjoy the full range of our products and services, we kindly request you complete the identity verification process.</code></td></tr><tr><td><code>20718</code></td><td><code>To enjoy the full range of our products and services, log in to your master account and complete the identity verification process.</code></td></tr><tr><td><code>20719</code></td><td><code>The number of requests from your IP address has exceeded the limit. Please try again later.</code></td></tr><tr><td><code>20720</code></td><td><code>Invalid authorization token.</code></td></tr><tr><td><code>20721</code></td><td><code>NFT ownership verification failed.</code></td></tr><tr><td><code>40701</code></td><td><code>Too many errors led to disconnected, please try again later.</code></td></tr><tr><td><code>40702</code></td><td><code>Too many requests. User-level rate limit exceeded, please retry later.</code></td></tr><tr><td><code>40703</code></td><td><code>Too many requests. System-level rate limit exceeded, please retry later.</code></td></tr><tr><td><code>40704</code></td><td><code>Too many requests. System-level concurrency limit exceeded, please retry later.</code></td></tr><tr><td><code>40705</code></td><td><code>Too many requests in a short period of time, please retry later.</code></td></tr><tr><td><code>50701</code></td><td><code>Internal error.</code></td></tr><tr><td><code>50702</code></td><td><code>The service has stopped running. Please disconnect:</code></td></tr><tr><td><code>50703</code></td><td><code>Server busy, please retry later.</code></td></tr><tr><td><code>50704</code></td><td><code>Gateway Timeout.</code></td></tr><tr><td><code>50705</code></td><td><code>unknown error.</code></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.kryptox.finance/api/websocket-trading-enpoints.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
