Skip to main content

Authentication

HashNut API v3.0.0 uses HMAC-SHA256 signature-based authentication for all API requests. This ensures secure, tamper-proof API communication.

Required Credentials

Before making API requests, you need:

  • accessKeyId: Your merchant access key ID (public identifier)
  • apiKey: Your API key (private secret for request signing)

Request Signature Generation

All authenticated API requests must include the following headers:

HeaderDescriptionExample
hashnut-request-uuidUnique UUID for the request550e8400-e29b-41d4-a716-446655440000
hashnut-request-timestampTimestamp (nonce) in milliseconds1704067200000
hashnut-request-signHMAC-SHA256 signature (Base64 encoded)<base64-encoded-signature>
Content-TypeRequest content typeapplication/json

Signature Algorithm

Step-by-Step Process

Algorithm Steps:

  1. Generate UUID: Create a unique UUID (e.g., 550e8400-e29b-41d4-a716-446655440000)
  2. Get Timestamp: Current timestamp in milliseconds (e.g., 1704067200000)
  3. Stringify Body: Convert request body to JSON string (no whitespace, exact format)
  4. Concatenate: Combine uuid + timestamp + body (no separators)
  5. Compute HMAC: HMAC-SHA256 using apiKey as secret
  6. Base64 Encode: Encode the HMAC result in Base64
  7. Set Header: Use Base64 string as hashnut-request-sign header value

Code Examples

import crypto from 'crypto';

function generateSignature(apiKey: string, body: object): {
uuid: string;
timestamp: string;
signature: string;
} {
// Step 1: Generate UUID
const uuid = crypto.randomUUID();

// Step 2: Get timestamp
const timestamp = Date.now().toString();

// Step 3: Stringify body (no whitespace)
const bodyString = JSON.stringify(body);

// Step 4: Concatenate
const signBody = uuid + timestamp + bodyString;

// Step 5 & 6: HMAC-SHA256 + Base64
const signature = crypto
.createHmac('sha256', apiKey)
.update(signBody)
.digest('base64');

return {
uuid,
timestamp,
signature
};
}

// Usage
const { uuid, timestamp, signature } = generateSignature(
'your-api-key',
{
accessKeyId: 'your-access-key-id',
merchantOrderId: 'order-123',
chainCode: 'erc20',
coinCode: 'usdt',
amount: 0.01
}
);

// Set headers
const headers = {
'hashnut-request-uuid': uuid,
'hashnut-request-timestamp': timestamp,
'hashnut-request-sign': signature,
'Content-Type': 'application/json'
};

Important Notes

Body Stringification

⚠️ Critical: The request body must be stringified exactly as it will be sent:

  • No extra whitespace: Use JSON.stringify(obj) or equivalent without formatting
  • Consistent ordering: Use the same key order (or use sorted keys)
  • Exact format: Match the exact JSON format that will be sent in the request

Timestamp Requirements

  • Format: Milliseconds since Unix epoch (e.g., 1704067200000)
  • Freshness: Timestamps should be recent (within 5 minutes)
  • Uniqueness: Combine with UUID to prevent replay attacks

UUID Requirements

  • Format: Standard UUID v4 format
  • Uniqueness: Each request must have a unique UUID
  • Generation: Use cryptographically secure random UUID generation

Complete Request Example

curl -X POST 'https://testnet.hashnut.io/api/v3.0.0/pay/createPayOrderOnSplitWalletWithApiKey' \
-H 'hashnut-request-uuid: 550e8400-e29b-41d4-a716-446655440000' \
-H 'hashnut-request-timestamp: 1704067200000' \
-H 'hashnut-request-sign: <base64-encoded-hmac-sha256>' \
-H 'Content-Type: application/json' \
-d '{
"accessKeyId": "YOUR_ACCESS_KEY_ID",
"merchantOrderId": "order-123",
"chainCode": "erc20",
"coinCode": "usdt",
"amount": 0.01
}'

Error Responses

Invalid Signature

{
"code": -2,
"msg": "Invalid signature or credentials",
"data": null
}

Common Causes:

  • Incorrect apiKey used for signing
  • Body stringified incorrectly (whitespace, key order)
  • Missing or incorrect headers
  • Timestamp format incorrect

Missing Headers

{
"code": -2,
"msg": "Missing required headers",
"data": null
}

Required Headers:

  • hashnut-request-uuid
  • hashnut-request-timestamp
  • hashnut-request-sign
  • Content-Type: application/json

Security Best Practices

  1. Secure Key Storage:

    • Never commit API keys to version control
    • Use environment variables or secrets management
    • Rotate keys regularly
  2. Signature Verification:

    • Always verify signatures match expected format
    • Use constant-time comparison for signature verification
    • Log authentication failures for monitoring
  3. Request Security:

    • Use HTTPS for all API requests
    • Implement request timeout handling
    • Validate all request parameters
  4. Key Management:

    • Use separate keys for testnet and production
    • Implement key rotation strategy
    • Monitor key usage for anomalies

Testing Authentication

Test Signature Generation

// Test with known values
const testBody = { accessKeyId: "test", amount: 1.0 };
const { uuid, timestamp, signature } = generateSignature("test-key", testBody);

console.log("UUID:", uuid);
console.log("Timestamp:", timestamp);
console.log("Signature:", signature);

Verify Signature Locally

// Verify your signature generation
function verifySignature(apiKey, uuid, timestamp, body, expectedSignature) {
const signBody = uuid + timestamp + JSON.stringify(body);
const computed = crypto
.createHmac('sha256', apiKey)
.update(signBody)
.digest('base64');

return computed === expectedSignature;
}

Next Steps


Ready to make API calls? Check out Order Endpoints →