Do not use production API keys and secrets on an online code compiler, including this website.

Run your requests and calculate your signature in a secure environment.

Introduction

Sending the API Signature instead of the API Secret protects the user from 'man-in-the-middle' attacks.

The API Signature is a hash-based message that contains the encrypted API Secret. It is generated based off HMAC-512.

Once generated, the signature can be used only once, so must be generated every time a request is made.

In all code examples outlined in this document the signature generation methods have been removed but the user will need to add them back in for the requests to work.

Example API Signature Methods

def gen_sig_helper(secret, data):
  secret_bytes = base64.b64decode(secret.encode("utf8"))
  return base64.b64encode(hmac.new(secret_bytes, data.encode("utf8"), digestmod = hashlib.sha512).digest()).decode("utf8")

def v3_gen_sig(secret, path, body_str = None):
  data = path
  if body_str != None:
    data = data + chr(0) + body_str
  return gen_sig_helper(secret, data)

def v4_gen_sig(secret, method, path, expires, body_str = None):
  data = method + path + str(expires)
  if body_str != None:
    data = data + body_str
  return gen_sig_helper(secret, data)
function gen_sig_helper(secret, data) {
  const secret_bytes = Buffer.from(secret, 'base64');
  return crypto.createHmac('sha512', secret_bytes).update(data).digest('base64');
}

function v3_gen_sig(secret, path, body_str) {
  let data = path;
  if (body_str) {
    data = data + '\0' + body_str;
  }
  return gen_sig_helper(secret, data);
}

function v4_gen_sig(secret, method, path, expires, body_str) {
  let data = method + path + expires;
  if (body_str) {
    data = data + body_str;
  }
  return gen_sig_helper(secret, data);
}

The user needs to send a path as well as the API secret. This path is whatever endpoint the user wishes to hit (minus the base URL).

For example gets the user's account information.

path = "api/3/account"
base_url = "https://trade-uk.sandbox.zodiamarkets.com"
key = '<key>'
secret = '<secret>'

def v3_mk_request(method, path, body = {}):
  print('=> ' + method + ' ' + path)
  tonce = int(time.time() * 1000 * 1000)
  body['tonce'] = tonce
  body_str = json.dumps(body)
  headers = {
    'Rest-Key': key,
    'Rest-Sign': v3_gen_sig(secret, path, body_str),
    'Content-Type': 'application/json'
  }
  response = requests.request(method, base_url + '/' + path, headers = headers, data = body_str)
  response_json = response.json()
  pprint.pprint(response_json)
  response.raise_for_status()
  return response_json
const path = "api/3/account"
const host = 'trade-uk.sandbox.zodiamarkets.com';
const key = '<key>';
const secret = '<secret>';

function v3_mk_request(method, path, body) {
  console.log(`=> ${method} ${path}`);
  return new Promise((resolve, reject) => {
    const tonce = Math.floor(Date.now() * 1000);
    body['tonce'] = tonce;
    const body_str = JSON.stringify(body);
    const headers = {
      'Rest-Key': key,
      'Rest-Sign': v3_gen_sig(secret, path, body_str),
      'Content-Type': 'application/json'
    }
    const opt = { host, method, path: '/' + path, headers };
    const req = https.request(opt, response_as_json(resolve, reject));
    req.write(body_str);
    req.end();
  });
}