Authentication

Sign every request with HMAC-SHA256 — headers, message format, and reference implementations.

Every request to the gateway is authenticated by an HMAC-SHA256 signature over the exact request. The secret itself is never transmitted.

Required headers

HeaderValue
x-bot-api-keyyour API key
x-bot-timestampcurrent Unix time in milliseconds, as a string
x-bot-signaturehex HMAC-SHA256 signature (below)
idempotency-keyrequired on order placement and cancel — a UUID you generate per trade
content-typeapplication/json on requests with a body

The signature

message   = timestamp + METHOD + path + rawBody
signature = hex( HMAC-SHA256( secretKey, message ) )
  • No separators between the four parts.
  • METHOD uppercase (POST, GET).
  • path is the URL path only, no query string — e.g. /bot/v1/spot/orders.
  • rawBody is the exact byte string you transmit. Empty string for bodyless requests (GET, cancel).
❗️

The golden rule: sign the bytes you send

Build the body as a string, sign that string, transmit that same string. Do not let an HTTP library re-serialize the body after signing — key reordering, added whitespace, or changed line endings will invalidate the signature. Interior formatting is fine as long as what you sign is what you send.

🚧

30-second replay window

The request is rejected with 401 if x-bot-timestamp is more than 30 seconds from server time. Keep your bot server's clock NTP-synced. The test endpoint returns serverTime so you can measure your skew.

Reference implementations

const crypto = require('crypto');

function signedHeaders(method, path, body /* string, '' if none */) {
  const timestamp = Date.now().toString();
  const message = timestamp + method.toUpperCase() + path + body;
  const signature = crypto
    .createHmac('sha256', process.env.WELLAT_BOT_SECRET_KEY)
    .update(message)
    .digest('hex');
  return {
    'x-bot-api-key': process.env.WELLAT_BOT_API_KEY,
    'x-bot-timestamp': timestamp,
    'x-bot-signature': signature,
    ...(body ? { 'content-type': 'application/json' } : {}),
  };
}
import hashlib, hmac, os, time

def signed_headers(method: str, path: str, body: str = "") -> dict:
    timestamp = str(int(time.time() * 1000))
    message = f"{timestamp}{method.upper()}{path}{body}"
    signature = hmac.new(
        os.environ["WELLAT_BOT_SECRET_KEY"].encode(),
        message.encode(),
        hashlib.sha256,
    ).hexdigest()
    headers = {
        "x-bot-api-key": os.environ["WELLAT_BOT_API_KEY"],
        "x-bot-timestamp": timestamp,
        "x-bot-signature": signature,
    }
    if body:
        headers["content-type"] = "application/json"
    return headers

Next: Testing Your Integration — verify your signing before you trade.


Did this page help you?