Authentication and Signature


Overview

To ensure security and data integrity, the OSL API uses an HMAC-SHA256 signature mechanism. Every private request must include specific authentication headers and a valid signature. Requests that fail validation will be rejected.

HTTP Headers

All authenticated requests must include the following headers:

Header NameRequiredDescription
ACCESS-KEYYesThe API key assigned to you.
ACCESS-SIGNYesThe generated HMAC-SHA256 signature (note that this is not the API secret, it must be processed).
ACCESS-TIMESTAMPYesThe Unix timestamp of the request in milliseconds.
ACCESS-PASSPHRASEYesThe passphrase you set during API key creation.

Timestamp Rules

  • Format: Must be a Unix timestamp in milliseconds (e.g., 1766066126559).
  • Synchronization: The platform validates the request time window. Ensure your server clock is synchronized via a standard NTP server.

Signature Generation

Algorithm

  • Type: HMAC-SHA256.
  • Encoding: The final result must be Base64 encoded.

String-to-Sign Structure

Concatenate the following fields in this exact order:

timestamp + HTTP_METHOD + request_path + query_string + request_body
ComponentDescription
timestampUnix timestamp in milliseconds.
HTTP_METHODThe HTTP method in uppercase (e.g., GET, POST).
request_pathThe path portion of the URL excluding the domain (e.g., /api/v2/trade/order).
query_stringThe raw query string including the ? (e.g., ?symbol=BTCUSDT).
request_bodyThe raw JSON string of the request body. For requests without a body (e.g., GET), use an empty string.
⚠️

Please note that due to the above requirements, the "Try It" console requires a manually generated HMAC signature.

Signature Generation Code Examples

import org.springframework.util.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class ApiExample {
    // Replace with your own credentials
    static final String BASE_URL = "https://api.osl.com";
    static final String ACCESS_KEY = "<your-access-key>";
    static final String SECRET_KEY = "<your-secret-key>";
    static final String PASSPHRASE = "<your-passphrase>";

    static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();

    public static void main(String[] args) throws Exception {
        queryReqIp();
        queryOrder();
    }

    static void queryReqIp() throws Exception {
        apiReq("GET", "/openapi/v1/ip", null, null);
    }

    static void queryOrder() throws Exception {
        apiReq("GET", "/openapi/v1/openOrders", null, null);
    }

    static void apiReq(String method, String path, String query, String body) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis());
        String sign = sign(timestamp, method, path, query, body);
        String url = BASE_URL + path + (StringUtils.hasText(query) ? "?" + query : "");
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("ACCESS-KEY", ACCESS_KEY)
                .header("ACCESS-SIGN", sign)
                .header("ACCESS-TIMESTAMP", timestamp)
                .header("ACCESS-PASSPHRASE", PASSPHRASE);
        switch (method.toUpperCase()) {
            case "GET":
                builder.method("GET", HttpRequest.BodyPublishers.noBody());
                break;
            case "POST":
            case "DELETE":
                builder.header("Content-Type", "application/json")
                        .method(method.toUpperCase(), HttpRequest.BodyPublishers.ofString(body != null ? body : ""));
                break;
        }
        System.out.println(HTTP_CLIENT.send(builder.build(), HttpResponse.BodyHandlers.ofString()).body());
    }

    static String sign(String timestamp, String method, String path, String query, String body) throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append(timestamp).append(method.toUpperCase()).append(path);
        if (query != null && !query.isEmpty()) {
            sb.append("?").append(query);
        }
        if (body != null && !body.isEmpty()) {
            sb.append(body);
        }
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        return Base64.getEncoder().encodeToString(mac.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8)));
    }

}
const CryptoJS = require('crypto-js');

const secret = pm.environment.get('API-SECRET');
const apiKey = pm.environment.get('API-KEY');
const apiPassphrase = pm.environment.get('API-PASSPHRASE');

const method = pm.request.method.toUpperCase();
const path = pm.request.url.getPath();  
const query = pm.request.url.getQueryString(); 

const rawBody = pm.request.body?.raw || '';
const timestamp = Date.now().toString();
const stringToSign = timestamp + method + path + 
                    (query ? '?' + query : '') + 
                    rawBody;

console.log('String to sign:', stringToSign);

// Generate HMAC SHA256 signature
const signature = CryptoJS.HmacSHA256(stringToSign, secret);
const accessSign = CryptoJS.enc.Base64.stringify(signature);

// Set the headers
pm.request.headers.add({
    key: 'ACCESS-KEY',
    value: apiKey
});
pm.request.headers.add({
    key: 'ACCESS-SIGN',
    value: accessSign
});
pm.request.headers.add({
    key: 'ACCESS-TIMESTAMP',
    value: timestamp
});
pm.request.headers.add({
    key: 'ACCESS-PASSPHRASE',
    value: apiPassphrase
});

// Debug output (optional)
console.log('ACCESS-SIGN:', accessSign);
console.log('Request headers info:', pm.request.headers);