Skip to main content

Overview

This guide provides detailed instructions for implementing HMAC-SHA256 signatures to authenticate requests to the HopNow API.
For a comprehensive authentication guide, see our Authentication documentation.

What is HMAC?

HMAC (Hash-based Message Authentication Code) is a cryptographic technique that ensures:
  • Authentication: Verifies the request comes from a legitimate source
  • Integrity: Confirms the request hasn’t been tampered with
  • Non-repudiation: Prevents denial of having sent the request

Step-by-Step Implementation

Step 1: Prepare the Payload

The HMAC signature is calculated over a payload consisting of:
{HTTP_METHOD}{FULL_URL}{TIMESTAMP}{REQUEST_BODY}

Components Breakdown

ComponentDescriptionExample
HTTP_METHODHTTP method in UPPERCASEPOST
FULL_URLComplete URL with protocolhttps://apis.hopnow.io/v1/customers/cus_123/accounts
TIMESTAMPUnix timestamp as string1640995200
REQUEST_BODYJSON request body (empty for GET){"name":"My Account"}

Example Payload

For this request:
POST https://apis.hopnow.io/v1/customers/cus_123/accounts
{
  "name": "Trading Account"
}
The payload would be:
POSThttps://apis.hopnow.io/v1/customers/cus_123/accounts1640995200{"name":"Trading Account"}

Step 2: Generate the Signature

Use HMAC-SHA256 with your API secret to sign the payload:
import hmac
import hashlib

def create_hmac_signature(method, url, timestamp, body, api_secret):
    """Create HMAC-SHA256 signature for API authentication"""
    # Construct payload
    payload = f"{method.upper()}{url}{timestamp}{body}"
    
    # Create signature
    signature = hmac.new(
        api_secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return signature

# Example usage
signature = create_hmac_signature(
    method="POST",
    url="https://apis.hopnow.io/v1/customers/cus_123/accounts",
    timestamp="1640995200",
    body='{"name":"Trading Account"}',
    api_secret="your_secret_key"
)
print(signature)

Step 3: Include Authentication Headers

Add the required headers to your request:
X-API-Key: your_api_key_id
X-Signature: calculated_hmac_signature
X-Timestamp: unix_timestamp

Common Implementation Mistakes

1. Incorrect Payload Construction

Wrong: Including spaces or formatting
payload = f"{method} {url} {timestamp} {body}"  # Don't add spaces!
Correct: No spaces between components
payload = f"{method}{url}{timestamp}{body}"

2. Wrong HTTP Method Case

Wrong: Using lowercase method
payload = f"{method.lower()}{url}{timestamp}{body}"  # Should be uppercase!
Correct: Always uppercase
payload = f"{method.upper()}{url}{timestamp}{body}"

3. JSON Body Formatting Issues

Wrong: Pretty-printed JSON
body = json.dumps(data, indent=2)  # Don't format!
Correct: Compact JSON
body = json.dumps(data, separators=(',', ':'))

4. Timestamp Mismatch

Wrong: Using different timestamps
timestamp_for_payload = str(int(time.time()))
timestamp_for_header = str(int(time.time()))  # Different value!
Correct: Same timestamp for both
timestamp = str(int(time.time()))
# Use same timestamp variable for both payload and header

Testing Your Implementation

Verification Steps

  1. Log the payload before signing to verify format
  2. Check timestamp is within 5 minutes of current time
  3. Verify signature matches expected output
  4. Test with known values using the examples below

Test Vectors

Use these test vectors to verify your implementation:

Test Case 1: Simple POST Request

Input:
  • Method: POST
  • URL: https://apis.hopnow.io/v1/test
  • Timestamp: 1640995200
  • Body: {"test":true}
  • Secret: test_secret_key_123
Expected Payload:
POSThttps://apis.hopnow.io/v1/test1640995200{"test":true}
Expected Signature:
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2

Test Case 2: GET Request (Empty Body)

Input:
  • Method: GET
  • URL: https://apis.hopnow.io/v1/customers/cus_123/accounts
  • Timestamp: 1640995200
  • Body: “ (empty string)
  • Secret: test_secret_key_123
Expected Payload:
GEThttps://apis.hopnow.io/v1/customers/cus_123/accounts1640995200

Debug Helper Function

Use this function to debug signature generation:
def debug_signature_generation(method, url, timestamp, body, api_secret):
    """Debug helper for HMAC signature generation"""
    payload = f"{method.upper()}{url}{timestamp}{body}"
    
    print("=== HMAC Signature Debug ===")
    print(f"Method: {method.upper()}")
    print(f"URL: {url}")  
    print(f"Timestamp: {timestamp}")
    print(f"Body: {body}")
    print(f"Payload: {payload}")
    print(f"Payload Length: {len(payload)}")
    
    signature = hmac.new(
        api_secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    print(f"Signature: {signature}")
    print("===========================")
    
    return signature

Security Best Practices

1. Secure Secret Storage

  • Store API secrets in environment variables or secure key management
  • Never hardcode secrets in your application code
  • Use different secrets for different environments

2. Timestamp Management

  • Use current timestamp for each request (don’t reuse)
  • Ensure your server clock is synchronized
  • Handle timezone properly (always use UTC)

3. Request Handling

  • Generate signatures just before sending requests
  • Don’t cache or reuse signatures
  • Implement proper error handling for authentication failures

4. Logging and Debugging

  • Log payload construction for debugging (but never log secrets)
  • Monitor authentication failures
  • Implement retry logic with exponential backoff

Troubleshooting

Signature Verification Failures

If you’re getting authentication errors:
  1. Double-check payload construction - Use the debug helper
  2. Verify timestamp is recent - Should be within 5 minutes
  3. Check HTTP method case - Must be uppercase in payload
  4. Validate JSON formatting - Should be compact, no extra whitespace
  5. Confirm URL is complete - Include full URL with https://

Common Error Messages

ErrorLikely CauseSolution
invalid_signaturePayload construction errorUse debug helper to verify payload
timestamp_too_oldTimestamp too oldUse current timestamp
missing_headersHeader not includedInclude all three required headers

Need additional help with HMAC implementation? Contact our support team with your debug output.