Calculate the API Signature
The following steps describe how to calculate the API signature.-
First, concatenate a string based on your request, as shown below:
str_to_sign =
{METHOD}|{PATH}|{TIMESTAMP}|{PARAMS}|{BODY}Field Description Example METHOD HTTP method. GETPATH API endpoint. /v2/transactions/transferTIMESTAMP Current Unix timestamp in milliseconds. This value must be identical to the nonce in the request header. 1718587017026PARAMS Query parameters. chain_id=ETH&limit=10BODY The raw request body as a string. {"wallet_type":"Custodial"}The PARAMS and BODY fields are optional. If a corresponding parameter is not present, leave it as an empty string. -
Use the
hashliblibrary to apply SHA-256 to the string twice, as shown below:import hashlib content_hash = hashlib.sha256(hashlib.sha256(str_to_sign.encode()).digest()).digest() -
Sign the string with your API Secret, as shown below:
from nacl.signing import SigningKey # Create an Ed25519 signing key. Replace `api_secret` with your API Secret. sk = SigningKey(bytes.fromhex(api_secret)) # Sign the hashed message signature = sk.sign(content_hash).signature.hex()
Signature Code Samples
Below are complete signature helper implementations in various languages. You can copy them directly into your project. Signature flow:- Concatenate the string to sign:
{METHOD}|{PATH}|{TIMESTAMP}|{PARAMS}|{BODY} - Double SHA256 hash:
sha256(sha256(str_to_sign)) - Sign with the Ed25519 private key
- Place the signature data in the request headers:
BIZ-API-KEY,Biz-Api-Nonce,Biz-Api-Signature
import hashlib
import time
import json
import requests
from nacl.signing import SigningKey
class NBTSigner:
"""NBT API signing helper"""
def __init__(self, api_key: str, api_secret: str, base_url: str = "https://apidev.nusd.me"):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = base_url
def sign(self, method: str, path: str, params: str = "", body: str = "") -> dict:
"""
Generate the signature and return request headers.
Args:
method: HTTP method (GET/POST)
path: API path (e.g. /nps/balance)
params: query string (e.g. wallet_id=xxx)
body: request body as JSON string
Returns:
A dict of headers containing the signature info.
"""
timestamp = str(int(time.time() * 1000))
str_to_sign = f"{method}|{path}|{timestamp}|{params}|{body}"
# Double SHA256
content_hash = hashlib.sha256(
hashlib.sha256(str_to_sign.encode()).digest()
).digest()
# Ed25519 signature
signing_key = SigningKey(bytes.fromhex(self.api_secret))
signature = signing_key.sign(content_hash).signature.hex()
return {
"BIZ-API-KEY": self.api_key,
"Biz-Api-Nonce": timestamp,
"Biz-Api-Signature": signature,
"Content-Type": "application/json",
}
def get(self, path: str, params: dict = None) -> dict:
"""Send a signed GET request"""
from urllib.parse import urlencode
query_string = urlencode(params) if params else ""
headers = self.sign("GET", path, params=query_string)
url = f"{self.base_url}{path}"
if query_string:
url += f"?{query_string}"
return requests.get(url, headers=headers).json()
def post(self, path: str, data: dict = None) -> dict:
"""Send a signed POST request"""
body = json.dumps(data, ensure_ascii=False) if data else ""
headers = self.sign("POST", path, body=body)
return requests.post(
f"{self.base_url}{path}", headers=headers, data=body
).json()
# Usage example
if __name__ == "__main__":
signer = NBTSigner(
api_key="your_api_key",
api_secret="your_api_secret_hex"
)
# GET request example
result = signer.get("/nps/balance", {"wallet_id": "your_wallet_id"})
print("Balance:", result)
# POST request example
result = signer.post("/nps/address", {
"wallet_id": "your_wallet_id",
"chain_id": "BASE_ETH",
"user_token": "user_123"
})
print("Address:", result)
const crypto = require('crypto');
const nacl = require('tweetnacl');
class NBTSigner {
/**
* NBT API signing helper
* @param {string} apiKey - API Key
* @param {string} apiSecret - API Secret (hex string)
* @param {string} baseURL - API base URL
*/
constructor(apiKey, apiSecret, baseURL = 'https://apidev.nusd.me') {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseURL = baseURL;
}
/**
* Generate the signature and return request headers
* @param {string} method - HTTP method (GET/POST)
* @param {string} path - API path
* @param {string} params - query string
* @param {string} body - request body JSON string
* @returns {Object} headers object
*/
sign(method, path, params = '', body = '') {
const timestamp = Date.now().toString();
const strToSign = `${method}|${path}|${timestamp}|${params}|${body}`;
// Double SHA256
const hash1 = crypto.createHash('sha256').update(strToSign).digest();
const hash2 = crypto.createHash('sha256').update(hash1).digest();
// Ed25519 signature
const secretKey = Buffer.from(this.apiSecret, 'hex');
const keyPair = nacl.sign.keyPair.fromSecretKey(secretKey);
const signature = nacl.sign.detached(hash2, keyPair.secretKey);
return {
'BIZ-API-KEY': this.apiKey,
'Biz-Api-Nonce': timestamp,
'Biz-Api-Signature': Buffer.from(signature).toString('hex'),
'Content-Type': 'application/json',
};
}
/**
* Send a signed GET request
*/
async get(path, params = {}) {
const queryString = new URLSearchParams(params).toString();
const headers = this.sign('GET', path, queryString);
const url = queryString
? `${this.baseURL}${path}?${queryString}`
: `${this.baseURL}${path}`;
const res = await fetch(url, { method: 'GET', headers });
return res.json();
}
/**
* Send a signed POST request
*/
async post(path, data = {}) {
const body = JSON.stringify(data);
const headers = this.sign('POST', path, '', body);
const res = await fetch(`${this.baseURL}${path}`, {
method: 'POST',
headers,
body,
});
return res.json();
}
}
// Usage example
(async () => {
const signer = new NBTSigner(
'your_api_key',
'your_api_secret_hex'
);
// GET request example
const balance = await signer.get('/nps/balance', {
wallet_id: 'your_wallet_id',
});
console.log('Balance:', balance);
// POST request example
const address = await signer.post('/nps/address', {
wallet_id: 'your_wallet_id',
chain_id: 'BASE_ETH',
user_token: 'user_123',
});
console.log('Address:', address);
})();
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
"golang.org/x/crypto/ed25519"
)
// NBTSigner is the NBT API signing helper
type NBTSigner struct {
APIKey string
APISecret string
BaseURL string
}
// NewNBTSigner creates a signing helper instance
func NewNBTSigner(apiKey, apiSecret string) *NBTSigner {
return &NBTSigner{
APIKey: apiKey,
APISecret: apiSecret,
BaseURL: "https://apidev.nusd.me",
}
}
// Sign generates the signature and returns request headers
func (s *NBTSigner) Sign(method, path, params, body string) http.Header {
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
strToSign := fmt.Sprintf("%s|%s|%s|%s|%s", method, path, timestamp, params, body)
// Double SHA256
hash1 := sha256.Sum256([]byte(strToSign))
hash2 := sha256.Sum256(hash1[:])
// Ed25519 signature
secretBytes, _ := hex.DecodeString(s.APISecret)
privateKey := ed25519.PrivateKey(secretBytes)
signature := ed25519.Sign(privateKey, hash2[:])
headers := http.Header{}
headers.Set("BIZ-API-KEY", s.APIKey)
headers.Set("Biz-Api-Nonce", timestamp)
headers.Set("Biz-Api-Signature", hex.EncodeToString(signature))
headers.Set("Content-Type", "application/json")
return headers
}
// Get sends a signed GET request
func (s *NBTSigner) Get(path string, params url.Values) ([]byte, error) {
queryString := params.Encode()
headers := s.Sign("GET", path, queryString, "")
fullURL := s.BaseURL + path
if queryString != "" {
fullURL += "?" + queryString
}
req, _ := http.NewRequest("GET", fullURL, nil)
req.Header = headers
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// Post sends a signed POST request
func (s *NBTSigner) Post(path string, data interface{}) ([]byte, error) {
bodyBytes, _ := json.Marshal(data)
body := string(bodyBytes)
headers := s.Sign("POST", path, "", body)
req, _ := http.NewRequest("POST", s.BaseURL+path, bytes.NewBufferString(body))
req.Header = headers
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func main() {
signer := NewNBTSigner("your_api_key", "your_api_secret_hex")
// GET request example
params := url.Values{}
params.Add("wallet_id", "your_wallet_id")
result, _ := signer.Get("/nps/balance", params)
fmt.Println("Balance:", string(result))
// POST request example
data := map[string]interface{}{
"wallet_id": "your_wallet_id",
"chain_id": "BASE_ETH",
"user_token": "user_123",
}
result, _ = signer.Post("/nps/address", data)
fmt.Println("Address:", string(result))
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.Map;
import java.util.stream.Collectors;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
/**
* NBT API signing helper
*/
public class NBTSigner {
private final String apiKey;
private final String apiSecret;
private final String baseURL;
private final HttpClient client = HttpClient.newHttpClient();
public NBTSigner(String apiKey, String apiSecret) {
this(apiKey, apiSecret, "https://apidev.nusd.me");
}
public NBTSigner(String apiKey, String apiSecret, String baseURL) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseURL = baseURL;
}
/**
* Generate the signature
*/
public String[] sign(String method, String path, String params, String body)
throws Exception {
String timestamp = String.valueOf(Instant.now().toEpochMilli());
String strToSign = String.format("%s|%s|%s|%s|%s",
method, path, timestamp, params, body);
// Double SHA256
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] hash1 = sha256.digest(strToSign.getBytes(StandardCharsets.UTF_8));
byte[] hash2 = sha256.digest(hash1);
// Ed25519 signature
byte[] secretBytes = hexToBytes(apiSecret);
Ed25519PrivateKeyParameters privateKey =
new Ed25519PrivateKeyParameters(secretBytes, 0);
Ed25519Signer signer = new Ed25519Signer();
signer.init(true, privateKey);
signer.update(hash2, 0, hash2.length);
byte[] signature = signer.generateSignature();
// Returns [timestamp, signatureHex]
return new String[]{timestamp, bytesToHex(signature)};
}
/**
* Send a signed GET request
*/
public String get(String path, Map<String, String> params) throws Exception {
String queryString = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8)
+ "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
String[] sig = sign("GET", path, queryString, "");
String url = baseURL + path + (queryString.isEmpty() ? "" : "?" + queryString);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.header("BIZ-API-KEY", apiKey)
.header("Biz-Api-Nonce", sig[0])
.header("Biz-Api-Signature", sig[1])
.build();
return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
}
/**
* Send a signed POST request
*/
public String post(String path, String jsonBody) throws Exception {
String[] sig = sign("POST", path, "", jsonBody);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseURL + path))
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.header("Content-Type", "application/json")
.header("BIZ-API-KEY", apiKey)
.header("Biz-Api-Nonce", sig[0])
.header("Biz-Api-Signature", sig[1])
.build();
return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
}
private static byte[] hexToBytes(String hex) {
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(
hex.substring(2 * i, 2 * i + 2), 16);
}
return bytes;
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
// Usage example
public static void main(String[] args) throws Exception {
NBTSigner signer = new NBTSigner(
"your_api_key", "your_api_secret_hex");
// GET request example
String balance = signer.get("/nps/balance",
Map.of("wallet_id", "your_wallet_id"));
System.out.println("Balance: " + balance);
// POST request example
String body = """
{"wallet_id":"your_wallet_id","chain_id":"BASE_ETH","user_token":"user_123"}
""".trim();
String address = signer.post("/nps/address", body);
System.out.println("Address: " + address);
}
}
#!/bin/bash
# NBT API signature helper script
# Dependencies: python3, pip install pynacl
# ============ Configuration ============
API_KEY="your_api_key"
API_SECRET="your_api_secret_hex"
BASE_URL="https://apidev.nusd.me"
# ============ Signing function ============
nbt_sign() {
local METHOD="$1" # GET or POST
local PATH="$2" # API path, e.g. /nps/balance
local PARAMS="$3" # query string (for GET)
local BODY="$4" # request body (for POST)
TIMESTAMP=$(python3 -c "import time; print(int(time.time()*1000))")
SIGNATURE=$(python3 -c "
import hashlib
from nacl.signing import SigningKey
str_to_sign = '${METHOD}|${PATH}|${TIMESTAMP}|${PARAMS}|${BODY}'
content_hash = hashlib.sha256(hashlib.sha256(str_to_sign.encode()).digest()).digest()
sk = SigningKey(bytes.fromhex('${API_SECRET}'))
print(sk.sign(content_hash).signature.hex())
")
echo "${TIMESTAMP}|${SIGNATURE}"
}
# ============ GET request ============
nbt_get() {
local PATH="$1"
local PARAMS="$2"
IFS='|' read -r TS SIG <<< "$(nbt_sign GET "$PATH" "$PARAMS" "")"
local URL="${BASE_URL}${PATH}"
[ -n "$PARAMS" ] && URL="${URL}?${PARAMS}"
curl -s -X GET "$URL" \
-H "BIZ-API-KEY: ${API_KEY}" \
-H "Biz-Api-Nonce: ${TS}" \
-H "Biz-Api-Signature: ${SIG}"
}
# ============ POST request ============
nbt_post() {
local PATH="$1"
local BODY="$2"
IFS='|' read -r TS SIG <<< "$(nbt_sign POST "$PATH" "" "$BODY")"
curl -s -X POST "${BASE_URL}${PATH}" \
-H "Content-Type: application/json" \
-H "BIZ-API-KEY: ${API_KEY}" \
-H "Biz-Api-Nonce: ${TS}" \
-H "Biz-Api-Signature: ${SIG}" \
-d "$BODY"
}
# ============ Usage examples ============
# GET request
echo "=== Query balance ==="
nbt_get "/nps/balance" "wallet_id=your_wallet_id"
# POST request
echo -e "\n=== Get deposit address ==="
nbt_post "/nps/address" '{"wallet_id":"your_wallet_id","chain_id":"BASE_ETH","user_token":"user_123"}'