> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.hopnow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# HMAC Signature Guide

> Step-by-step guide to implementing HMAC-SHA256 signatures for API authentication

## Overview

This guide explains the exact HMAC-SHA256 signing contract used by the HopNow Client API.

<Info>
  For the complete authentication overview, see [Authentication](/fx/authentication).
</Info>

## Signing Contract

Every client API request must include these headers:

```text theme={null}
X-API-Key: your_api_key
X-Timestamp: unix_timestamp
X-Nonce: unique_32_character_hex_nonce
X-Signature: calculated_hmac_signature
```

The signature is calculated over this payload:

```text theme={null}
{METHOD}{PATH_AND_QUERY}{TIMESTAMP}{NONCE}{BODY}
```

| Component        | Description                                                                                                                                                       | Example                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `METHOD`         | HTTP method in uppercase                                                                                                                                          | `POST`                                  |
| `PATH_AND_QUERY` | The URL path exactly as sent to HopNow, starting with `/`, followed by `?` and the query string when present. Do not include the scheme, host, port, or fragment. | `/v1/customers/cus_123/accounts?page=1` |
| `TIMESTAMP`      | Unix timestamp as a string                                                                                                                                        | `1640995200`                            |
| `NONCE`          | Unique random 32-character hex string                                                                                                                             | `a1b2c3d4e5f678901234567890abcdef`      |
| `BODY`           | Exact request body string sent over the wire. Use an empty string for requests without a body.                                                                    | `{"name":"Trading Account"}`            |

Use the API key's `secret` as the HMAC key. The `api_key` value goes in the `X-API-Key` header.

Examples:

| Request URL                                                          | Sign this `PATH_AND_QUERY` value                |
| -------------------------------------------------------------------- | ----------------------------------------------- |
| `https://api.hopnow.io/v1/test`                                      | `/v1/test`                                      |
| `https://api.hopnow.io/v1/customers/cus_123/accounts?page=1&size=10` | `/v1/customers/cus_123/accounts?page=1&size=10` |

## Example Payload

For this request:

```bash theme={null}
POST https://api.hopnow.io/v1/customers/cus_123/accounts
Content-Type: application/json

{"name":"Trading Account"}
```

With:

```text theme={null}
timestamp = 1640995200
nonce = abcdef0123456789abcdef0123456789
```

The payload to sign is:

```text theme={null}
POST/v1/customers/cus_123/accounts1640995200abcdef0123456789abcdef0123456789{"name":"Trading Account"}
```

## Implementation Examples

<CodeGroup>
  ```python Python theme={null}
  import hashlib
  import hmac
  import json
  import secrets
  import time

  import requests


  def compact_json(data):
      return json.dumps(data, separators=(",", ":"))


  def create_signature(method, path_and_query, timestamp, nonce, body, secret):
      payload = f"{method.upper()}{path_and_query}{timestamp}{nonce}{body}"
      return hmac.new(
          secret.encode("utf-8"),
          payload.encode("utf-8"),
          hashlib.sha256,
      ).hexdigest()


  api_key = "your_api_key"
  secret = "your_secret"
  base_url = "https://api.hopnow.io"
  path_and_query = "/v1/customers/cus_123/accounts"
  body = compact_json({"name": "Trading Account"})
  timestamp = str(int(time.time()))
  nonce = secrets.token_hex(16)
  signature = create_signature("POST", path_and_query, timestamp, nonce, body, secret)

  response = requests.post(
      f"{base_url}{path_and_query}",
      headers={
          "Content-Type": "application/json",
          "X-API-Key": api_key,
          "X-Timestamp": timestamp,
          "X-Nonce": nonce,
          "X-Signature": signature,
      },
      data=body,
  )
  response.raise_for_status()
  ```

  ```javascript JavaScript theme={null}
  const crypto = require('crypto');

  function createSignature(method, pathAndQuery, timestamp, nonce, body, secret) {
    const payload = `${method.toUpperCase()}${pathAndQuery}${timestamp}${nonce}${body}`;
    return crypto
      .createHmac('sha256', secret)
      .update(payload)
      .digest('hex');
  }

  const apiKey = 'your_api_key';
  const secret = 'your_secret';
  const baseUrl = 'https://api.hopnow.io';
  const pathAndQuery = '/v1/customers/cus_123/accounts';
  const body = JSON.stringify({ name: 'Trading Account' });
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomBytes(16).toString('hex');
  const signature = createSignature('POST', pathAndQuery, timestamp, nonce, body, secret);

  fetch(`${baseUrl}${pathAndQuery}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': apiKey,
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
      'X-Signature': signature,
    },
    body,
  });
  ```

  ```bash cURL theme={null}
  API_KEY="your_api_key"
  SECRET="your_secret"
  BASE_URL="https://api.hopnow.io"
  METHOD="POST"
  PATH_AND_QUERY="/v1/customers/cus_123/accounts"
  TIMESTAMP=$(date +%s)
  NONCE=$(openssl rand -hex 16)
  BODY='{"name":"Trading Account"}'

  PAYLOAD="${METHOD}${PATH_AND_QUERY}${TIMESTAMP}${NONCE}${BODY}"
  SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex | cut -d' ' -f2)

  curl -X "$METHOD" "${BASE_URL}${PATH_AND_QUERY}" \
    -H "Content-Type: application/json" \
    -H "X-API-Key: $API_KEY" \
    -H "X-Timestamp: $TIMESTAMP" \
    -H "X-Nonce: $NONCE" \
    -H "X-Signature: $SIGNATURE" \
    -d "$BODY"
  ```
</CodeGroup>

## Test Vectors

Use these fixed inputs to validate your local implementation before making API calls.

### Test Case 1: POST Request

| Input          | Value                              |
| -------------- | ---------------------------------- |
| Method         | `POST`                             |
| Path and query | `/v1/test`                         |
| Timestamp      | `1640995200`                       |
| Nonce          | `a1b2c3d4e5f678901234567890abcdef` |
| Body           | `{"test":true}`                    |
| Secret         | `test_secret_key_123`              |

Expected payload:

```text theme={null}
POST/v1/test1640995200a1b2c3d4e5f678901234567890abcdef{"test":true}
```

Expected signature:

```text theme={null}
91d30335d4bfdc5e0801f31de86e34e5ecc557aca551650f42141dd06eeb9953
```

### Test Case 2: GET Request With Query String

| Input          | Value                                           |
| -------------- | ----------------------------------------------- |
| Method         | `GET`                                           |
| Path and query | `/v1/customers/cus_123/accounts?page=1&size=10` |
| Timestamp      | `1640995200`                                    |
| Nonce          | `00112233445566778899aabbccddeeff`              |
| Body           | empty string                                    |
| Secret         | `test_secret_key_123`                           |

Expected payload:

```text theme={null}
GET/v1/customers/cus_123/accounts?page=1&size=10164099520000112233445566778899aabbccddeeff
```

Expected signature:

```text theme={null}
15decbbdf4fa8f2a26998e95a4c95f75bfa19cab74c8f2c2f08ff6ef191800b4
```

## Common Mistakes

* Signing the full URL. Sign `/v1/...`, not `https://api.hopnow.io/v1/...`.
* Omitting `X-Nonce` or leaving the nonce out of the payload.
* Signing a pretty-printed JSON body but sending compact JSON, or vice versa.
* Signing query parameters in a different order than the actual request URL.
* Reusing a nonce within the 5-minute replay window.
* Sending a Bearer token instead of the four HMAC headers.

## Troubleshooting Checklist

1. Log the exact payload before signing.
2. Confirm the signed path starts with `/` and includes the query string exactly as sent.
3. Confirm the timestamp header matches the timestamp in the payload.
4. Confirm the nonce header matches the nonce in the payload.
5. Confirm the body string is byte-for-byte the same string sent in the request.
6. Confirm the HMAC key is the API key `secret`, not the `api_key` header value.
