> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qfex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticate

Connections to `trade.qfex.com` require authentication. You must authenticate **within 1 minute** of connecting or the server will close the connection. Authentication uses an HMAC-SHA256 signature or a valid JWT token.

## How it works

1. Connect to **wss\://trade.qfex.com?api\_key=YOUR\_PUBLIC\_KEY**.
2. Generate a cryptographically secure random nonce (hex encoded, max 100 characters) and capture the current Unix timestamp.
3. Build the string `${nonce}:${unix_ts}` and compute an HMAC-SHA256 using your secret key. Hex-encode the result to get the signature.
4. Send the auth payload with the `hmac` block shown below. The nonce must be unique within a 15 minute window.
5. If you want the authenticated Trade WebSocket session to use a subaccount, include an optional `account_id` field alongside either `hmac` or `jwt`. When omitted, the connection uses the primary account.

```json theme={null}
{
  "type": "auth",
  "params": {
    "hmac": {
      "public_key": "qfex_pub_xxxxx", // Your public (API) key
      "nonce": "c0ffee...", // Secure random hex string
      "unix_ts": 1760545414, // Current Unix timestamp (seconds)
      "signature": "5f2e..." // Hex-encoded HMAC-SHA256(secret, "nonce:unix_ts")
    },
    // optional
    "account_id": "11111111-1111-1111-1111-111111111111"
  }
}
```

Alternatively, authenticate with a JWT:

```json theme={null}
{
  "type": "auth",
  "params": {
    "jwt": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImNiYzVmZWNmLTlhODItNGFlNy04NDFkLTBkMTdjMjUzMWM3OCIsInR5cCI6IkpXVCJ9...",
    // optional
    "account_id": "11111111-1111-1111-1111-111111111111"
  }
}
```

## Sample Code

<CodeGroup>
  ```python python theme={null}
  # Python (websocket-client)
  # pip install websocket-client
  import hashlib
  import hmac
  import json
  import secrets
  import time

  import websocket

  PUB_KEY = "qfex_pub_xxxxx"
  SECRET_KEY = "qfex_secret_yyyyyy"


  def build_auth_message():
      nonce = secrets.token_hex(16)
      unix_ts = int(time.time())
      payload = f"{nonce}:{unix_ts}".encode()
      signature = hmac.new(SECRET_KEY.encode(), payload, hashlib.sha256).hexdigest()
      return {
          "type": "auth",
          "params": {
              "hmac": {
                  "public_key": PUB_KEY,
                  "nonce": nonce,
                  "unix_ts": unix_ts,
                  "signature": signature,
              }
          },
      }


  def on_open(ws):
      ws.send(json.dumps(build_auth_message()))


  def on_message(_, message):
      print("Message:", message)


  def on_error(_, error):
      print("Error:", error)


  def on_close(_, code, reason):
      print("Closed:", code, reason)


  ws = websocket.WebSocketApp(
      "wss://trade.qfex.com?api_key=" + PUB_KEY,
      on_open=on_open,
      on_message=on_message,
      on_error=on_error,
      on_close=on_close,
  )
  ws.run_forever()
  ```

  ```javascript node theme={null}
  // Node.js (ws)
  // npm i ws
  import crypto from "crypto";
  import WebSocket from "ws";

  const pubKey = "qfex_pub_xxxxx";
  const secretKey = "qfex_secret_yyyyyy";

  function buildAuthMessage() {
    const nonce = crypto.randomBytes(16).toString("hex");
    const unixTs = Math.floor(Date.now() / 1000);
    const payload = `${nonce}:${unixTs}`;
    const signature = crypto
      .createHmac("sha256", secretKey)
      .update(payload)
      .digest("hex");

    return {
      type: "auth",
      params: {
        hmac: {
          public_key: pubKey,
          nonce,
          unix_ts: unixTs,
          signature,
        },
      },
    };
  }

  const ws = new WebSocket(`wss://trade.qfex.com?api_key=${pubKey}`);

  ws.on("open", () => {
    ws.send(JSON.stringify(buildAuthMessage()));
  });

  ws.on("message", (msg) => {
    console.log("Message:", msg.toString());
  });

  ws.on("error", (err) => console.error("WS error:", err));
  ws.on("close", (code, reason) => {
    console.log("Closed:", code, reason?.toString() ?? "");
  });
  ```

  ```go go theme={null}
  // Go (gorilla/websocket)
  // go get github.com/gorilla/websocket
  package main

  import (
  	"crypto/hmac"
  	"crypto/rand"
  	"crypto/sha256"
  	"encoding/hex"
  	"fmt"
  	"log"
  	"os"
  	"os/signal"
  	"time"

  	"github.com/gorilla/websocket"
  )

  func main() {
  	pubKey := "qfex_pub_xxxxx"
  	secretKey := []byte("qfex_secret_yyyyyy")

  	c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key="+pubKey, nil)
  	if err != nil {
  		log.Fatal("dial:", err)
  	}
  	defer c.Close()

  	nonceBytes := make([]byte, 16)
  	if _, err := rand.Read(nonceBytes); err != nil {
  		log.Fatal("nonce:", err)
  	}
  	nonce := hex.EncodeToString(nonceBytes)
  	unixTs := time.Now().Unix()
  	message := fmt.Sprintf("%s:%d", nonce, unixTs)

  	h := hmac.New(sha256.New, secretKey)
  	h.Write([]byte(message))
  	signature := hex.EncodeToString(h.Sum(nil))

  	auth := map[string]any{
  		"type": "auth",
  		"params": map[string]any{
  			"hmac": map[string]any{
  				"public_key": pubKey,
  				"nonce":      nonce,
  				"unix_ts":    unixTs,
  				"signature":  signature,
  			},
  		},
  	}

  	if err := c.WriteJSON(auth); err != nil {
  		log.Fatal("write auth:", err)
  	}

  	go func() {
  		for {
  			_, message, err := c.ReadMessage()
  			if err != nil {
  				log.Println("read:", err)
  				return
  			}
  			fmt.Printf("Message: %s\n", message)
  		}
  	}()

  	interrupt := make(chan os.Signal, 1)
  	signal.Notify(interrupt, os.Interrupt)
  	<-interrupt
  }
  ```

  ```java java theme={null}
  // Java (OkHttp WebSocket)
  // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0")
  import java.nio.charset.StandardCharsets;
  import java.security.SecureRandom;
  import java.util.concurrent.TimeUnit;
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import okhttp3.*;

  public class AuthWs {
    private static final String PUB_KEY = "qfex_pub_xxxxx";
    private static final String SECRET_KEY = "qfex_secret_yyyyyy";

    public static void main(String[] args) {
      OkHttpClient client = new OkHttpClient.Builder()
          .pingInterval(20, TimeUnit.SECONDS)
          .build();

      Request request = new Request.Builder()
          .url("wss://trade.qfex.com?api_key=" + PUB_KEY)
          .build();

      WebSocketListener listener = new WebSocketListener() {
        @Override public void onOpen(WebSocket ws, Response response) {
          ws.send(buildAuthPayload());
        }

        @Override public void onMessage(WebSocket ws, String text) {
          System.out.println("Message: " + text);
        }

        @Override public void onFailure(WebSocket ws, Throwable t, Response r) {
          System.err.println("WS error: " + t.getMessage());
        }
      };

      client.newWebSocket(request, listener);

      try {
        Thread.sleep(Long.MAX_VALUE);
      } catch (InterruptedException ignored) {
        Thread.currentThread().interrupt();
      }
    }

    private static String buildAuthPayload() {
      SecureRandom random = new SecureRandom();
      byte[] nonceBytes = new byte[16];
      random.nextBytes(nonceBytes);
      String nonce = bytesToHex(nonceBytes);

      long unixTs = System.currentTimeMillis() / 1000;
      String data = nonce + ":" + unixTs;

      String signature;
      try {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(keySpec);
        signature = bytesToHex(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
      } catch (Exception e) {
        throw new IllegalStateException("Unable to sign payload", e);
      }

      return String.format(
          "{\"type\":\"auth\",\"params\":{\"hmac\":{\"public_key\":\"%s\",\"nonce\":\"%s\",\"unix_ts\":%d,\"signature\":\"%s\"}}}",
          PUB_KEY, nonce, unixTs, signature);
    }

    private static String bytesToHex(byte[] bytes) {
      StringBuilder sb = new StringBuilder(bytes.length * 2);
      for (byte b : bytes) {
        sb.append(String.format("%02x", b));
      }
      return sb.toString();
    }
  }
  ```
</CodeGroup>

## Example Response

```json theme={null}
{ "type": "auth", "result": "success" }
```

<Note>
  After the client successfully authenticates, the server keeps the connection
  alive by periodically sending heartbeat messages in the form of [WebSocket
  ping
  frames](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#pings_and_pongs_the_heartbeat_of_websockets).
</Note>
