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

# HMAC-SHA256 Authentication: XPayLabs API Security

> XPayLabs authenticates every API request with HMAC-SHA256 request signing. Compute the sign field with your merchant token for secure crypto payment API calls.

XPayLabs authenticates every API request using HMAC-SHA256 request signing. Unlike traditional Bearer token authentication, XPayLabs requires each request to include a cryptographic signature computed over the request payload. This ensures request integrity and prevents replay attacks.

***

## What is the HMAC-SHA256 signing format?

Every API request includes a `ReqPayload` wrapper with four fields:

| Field       | Type    | Description                                              |
| ----------- | ------- | -------------------------------------------------------- |
| `sign`      | string  | HMAC-SHA256 signature of the stringified `data` object   |
| `timestamp` | integer | Unix timestamp (seconds) of when the request was created |
| `nonce`     | string  | A unique random string, never reused                     |
| `data`      | object  | The actual request payload                               |

The `sign` field is computed using your merchant token as the HMAC secret key.

***

## How to compute the HMAC signature

### Algorithm

1. Serialize the `data` object as a JSON string (no pretty-printing, no extra whitespace).
2. Compute `HMAC-SHA256(data_json, merchant_token)`.
3. Convert the result to a lowercase hex string.
4. Set this value as the `sign` field.

### Node.js

```javascript theme={null}
import crypto from "crypto";

const MERCHANT_TOKEN = process.env.XPAYLABS_MERCHANT_TOKEN;

function signRequest(data) {
  const json = JSON.stringify(data);   // compact JSON, no extra whitespace
  return crypto
    .createHmac("sha256", MERCHANT_TOKEN)
    .update(json, "utf8")
    .digest("hex");
}

const request = {
  sign: signRequest({ amount: "100.00", symbol: "USDT", chain: "TRON" }),
  timestamp: Math.floor(Date.now() / 1000),
  nonce: crypto.randomUUID(),
  data: { amount: "100.00", symbol: "USDT", chain: "TRON" },
};
```

### Python

```python theme={null}
import hmac
import hashlib
import json
import time
import uuid

MERCHANT_TOKEN = "your-merchant-token"

def sign_request(data: dict) -> str:
    json_str = json.dumps(data, separators=(",", ":"))  # compact JSON
    return hmac.new(
        MERCHANT_TOKEN.encode("utf-8"),
        json_str.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

request = {
    "sign": sign_request({"amount": "100.00", "symbol": "USDT", "chain": "TRON"}),
    "timestamp": int(time.time()),
    "nonce": str(uuid.uuid4()),
    "data": {"amount": "100.00", "symbol": "USDT", "chain": "TRON"},
}
```

### Go

```go theme={null}
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "time"
    "github.com/google/uuid"
)

func signRequest(data interface{}, token string) string {
    jsonBytes, _ := json.Marshal(data)
    mac := hmac.New(sha256.New, []byte(token))
    mac.Write(jsonBytes)
    return hex.EncodeToString(mac.Sum(nil))
}

func main() {
    data := map[string]interface{}{
        "amount":  "100.00",
        "symbol":  "USDT",
        "chain":   "TRON",
    }
    request := map[string]interface{}{
        "sign":      signRequest(data, "your-merchant-token"),
        "timestamp": time.Now().Unix(),
        "nonce":     uuid.New().String(),
        "data":      data,
    }
    fmt.Printf("%+v\n", request)
}
```

<Warning>
  The JSON serialization **must be compact** — no spaces, no newlines between key-value pairs. Different JSON serializers may produce different output; always test your signature computation against a known working example.
</Warning>

***

## What are the timestamp and nonce requirements?

The `timestamp` and `nonce` fields prevent replay attacks:

* **Timestamp**: Your server clock must be within **5 minutes** of XPayLabs server time. Requests with timestamps older than 5 minutes are rejected.
* **Nonce**: Each request must use a unique nonce. XPayLabs tracks used nonces and rejects duplicates. UUIDs or cryptographically random strings work well.

***

## How does XPayLabs verify signatures server-side?

XPayLabs verifies every request by recomputing the HMAC-SHA256 signature using your stored merchant token. If the signatures don't match, or if the timestamp is outside the tolerance window, the request is rejected with a `401 Unauthorized` response.

***

## How to keep your merchant token secure

* Store your merchant token in an environment variable or secrets manager.
* Never hardcode the token in source code or client-side applications.
* Rotate the token periodically and update your configuration.
* The token is a shared secret between your merchant server and the XPayLabs gateway. It is not sent over the network in API requests.
