Skip to content
Longport Whale
Users, Accounts, and APIs

Broker API Technical Integration

Authentication, WebSocket, connection flow, and Go request example

Broker API implements securities back-office operations, mainly covering users, accounts, assets, trading, and IPO subscriptions. See Broker API for the complete interface reference.

Authentication

Broker API uses a set of Access Key credentials for institutional authentication.

Credential Purpose
AccessKeyId Sent in the x-api-key header with every request
AccessKeySecret Used by Broker Server to generate the request signature; never sent directly
AccessKeyToken Sent as the Access Token in the authorization header

Broker API verifies that the Access Key exists, the request signature is correct, the source IP is allowlisted, the Access Token is valid, and the request is within the rate limit.

Request headers

Header Description Example
x-api-key Access Key ID <access-key-id>
authorization Access Token <access-token>
x-timestamp Request timestamp 1685518362.111
x-api-signature Request signature HMAC-SHA256 SignedHeaders=x-api-key;authorization;x-timestamp,Signature=<signature>

Because this is Server-to-Server communication, session and refresh-session flows do not apply. The basic request flow is:

sequenceDiagram
    participant Broker as Broker Server
    participant API as Broker API
    Broker->>Broker: Generate timestamp and HMAC-SHA256 signature
    Broker->>API: Send Access Key, Token, timestamp, and signature
    API->>API: Verify Key, signature, IP allowlist, Token, and rate limit
    alt Validation succeeds
        API-->>Broker: Business response
    else Validation fails
        API-->>Broker: HTTP status and business error code
    end

WebSocket trade notifications

Whale provides a WebSocket trade gateway for order-status updates. The Broker API WebSocket trade gateway follows the same connection model as the OpenAPI WebSocket trade gateway, and its private data protocol follows the OpenAPI protocol. Broker submits orders through Broker API and receives order-status updates through the WebSocket gateway.

References:

Initial connection

Obtain an OTP

Call the OTP API.

Open the WebSocket

Connect to the trade-gateway address confirmed for the project.

Authenticate

Use the OTP in the auth command. A successful response returns the Session ID used for reconnection.

Subscribe to trade messages

Subscribe to the private topic to receive order-status changes.

Reconnection

Open a new WebSocket

Create a new WebSocket connection.

Reconnect with the Session ID

Use the previously stored Session ID in the reconnect command. A successful response returns a new Session ID for the next reconnection.

Restore the subscription

Subscribe to the private topic again, and use order_id to handle order messages that may be delivered more than once.

Historical Go request example

The complete example from the original integration material is preserved below. It demonstrates configuration, HTTP requests, and WebSocket subscription together, but must not be used in a current project without review and modification. Confirm that the client library is still supported, then replace all addresses and credentials with the project’s actual configuration.

package main

import (
        "context"
        "encoding/json"
        "log"
        "net/url"
        std_http "net/http"
        "syscall"
        "time"
        "os"
        "os/signal"

        "github.com/longbridgeapp/openapi-go/config"
        "github.com/longbridgeapp/openapi-go/http"
        "github.com/longbridgeapp/openapi-go/trade"
)

var (
        accessKeyId     = ""
        accessKeySecret = ""
        accessToken     = ""

        httpAddr           = "https://openapi.longbridge.xyz"
        tradeWebsocketAddr = "wss://openapi-trade.longbridge.xyz"
)

func main() {
        // init config
        conf := &config.Config{
                AppKey:      accessKeyId,
                AppSecret:   accessKeySecret,
                AccessToken: accessToken,
                HttpURL:     httpAddr,
                TradeUrl:    tradeWebsocketAddr,
                LogLevel:    "warn",
        }

        // init go std http client
        hc := &std_http.Client{
                Transport: &std_http.Transport{
                        MaxIdleConnsPerHost: 20,
                },
                Timeout: 10 * time.Second,
        }
        conf.Client = hc

        // new trade context
        // this operation will establish websocket connection to trade gateway
        tctx, err := trade.NewFromCfg(conf)

        if err != nil {
                log.Fatal(err)
        }

        // subscribe private topic to receive order change events
        if _, err = tctx.Subscribe(context.Background(), []string{"private"}); err != nil {
                log.Fatal(err)
        }

        // register order change event callback
        tctx.OnTrade(func(ev *trade.PushEvent) {
                log.Println("received trade event")
        })

        // new http client for rest api calling
        client, err := http.New(http.WithAppKey(conf.AppKey), http.WithAppSecret(conf.AppSecret), http.WithURL(conf.HttpURL), http.WithAccessToken(conf.AccessToken))

        if err != nil {
                log.Fatal(err)
        }

        // create order
        createOrder(client)

        ch := make(chan os.Signal, 1)
        signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
        s := <-ch
        log.Println("exit for " + s.String())
}

func createOrder(c *http.Client) {
        doRequest(c, Request{
                Name:   "SubmitOrder",
                Method: std_http.MethodPost,
                Path:   "/v1/whaleapi/trade/order",
                Parameters: Parameters{
                        Body: map[string]interface{}{
                                "account_no":         "<account-no>",
                                "symbol":             "AAPL.US",
                                "order_type":         "MO",
                                "submitted_quantity": "100",
                                "side":               "Buy",
                                "time_in_force":      "Day",
                        },
                },
        })
}

type Request struct {
        Name       string
        Method     string
        Path       string
        Parameters Parameters
}

type Parameters struct {
        Query  map[string]string
        Header map[string]string
        Body   map[string]interface{}
}

func doRequest(c *http.Client, req Request) {
        var resp json.RawMessage

        log.Println("calling api " + req.Name)

        q := make(url.Values)

        for k, v := range req.Parameters.Query {
                q.Set(k, v)
        }

        var body interface{}

        if len(req.Parameters.Body) != 0 {
                body = req.Parameters.Body
        }

        // call api
        err := c.Call(context.Background(), req.Method, req.Path, q, body, &resp)

        if err != nil {
                log.Printf("[ERR] failed to invoke %s error: %v\n", req.Name, err)
        } else {
                log.Println(req.Name + " resp: " + string(resp))
        }

}

Save the code as a local Go file, then run these commands in the same directory:

go mod init main
go mod tidy
go run ./

Example output:

Go example output

Initialize configuration

conf := &config.Config{
                AppKey:      accessKeyId,
                AppSecret:   accessKeySecret,
                AccessToken: accessToken,
                HttpURL:     httpAddr,
                TradeUrl:    tradeWebsocketAddr,
                LogLevel:    "warn",
        }

Initialize the trade context

// init go std http client
        hc := &std_http.Client{
                Transport: &std_http.Transport{
                        MaxIdleConnsPerHost: 20,
                },
                Timeout: 10 * time.Second,
        }
        conf.Client = hc

        // new trade context
        // this operation will establish websocket connection to trade gateway
        tctx, err := trade.NewFromCfg(conf)

        if err != nil {
                log.Fatal(err)
        }

Connect and subscribe to order events

// subscribe private topic to receive order change events
        if _, err = tctx.Subscribe(context.Background(), []string{"private"}); err != nil {
                log.Fatal(err)
        }

        // register order change event callback
        tctx.OnTrade(func(ev *trade.PushEvent) {
                log.Println("received trade event")
        })

Call an HTTP API

// new http client for rest api calling
        client, err := http.New(http.WithAppKey(conf.AppKey), http.WithAppSecret(conf.AppSecret), http.WithURL(conf.HttpURL), http.WithAccessToken(conf.AccessToken))

        if err != nil {
                log.Fatal(err)
        }

        // create order
        createOrder(client)

Historical integration FAQs

Whale Docs