How to Work with API Keys

Use HMAC-signed API keys for direct server-to-server communication with the Asterium Pay API. This guide covers key creation, request signing, and signature validation.

API Key Management Endpoints

EndpointMethodDescription
List API KeysGET merchant/api-keysRetrieve all active API keys
Create API KeyPOST merchant/api-keysGenerate a new pk_ / sk_ key pair
Validate RequestPOST merchant/api-keys/validate-requestTest your HMAC signature implementation
Delete API KeyDELETE merchant/:idRevoke an API key pair
🚧

Remember

The secretKey (sk_...) is displayed only once at creation time. Store it immediately in a secure vault or secrets manager.



📘

Important Notes

  • ⏱️ X-Timestamp must be within ±60 seconds of server time (replay protection).
  • 🔒 Query parameters must be in the exact order as in the URL.
  • 🧾 Body hash uses the raw JSON string — no whitespace normalization.
  • 🧮 Signature must be a lowercase hexadecimal string (64 characters).
  • 🛣️ Service prefixes (e.g. /crypto-acquiring, /fiscalization) must be removed from request_path before signature calculation.

Authentication Headers

Each request must include the following headers:

HeaderDescriptionExample
X-API-KeyYour public API keypk_abc123xyz
X-SignatureHMAC-SHA256 signature (64-character lowercase hex)b4f2c3a4d8e9...
X-TimestampUnix timestamp in seconds1696248123
Timestamp calculation
timestamp=$(date +%s)
import time

def make_signed_request(method, path, body=None, query=""):
    timestamp = str(int(time.time()))
    headers = {
        "X-Timestamp": timestamp
    }
async function makeSignedRequest(method, path, body = null, query = "") {
  const timestamp = Math.floor(Date.now() / 1000).toString();

  const response = await fetch(url, {
    method,
    headers: {
      "X-Timestamp": timestamp,
    }
  });
  return response.json();
}
Signature Calculation

The signature is calculated as:

signature = HMAC_SHA256(payload, secret_key)

where payload depends on the HTTP method.

Payload Summary

MethodFormula
GETtimestamp + path + query
DELETEtimestamp + path + query
POSTtimestamp + path + query + SHA256(body)
PUTtimestamp + path + query + SHA256(body)
POST (multipart)timestamp + path + query

🚧

Important

  • Query string is appended without ?

Request Signing Examples

GET / DELETE Requests

Payload formula example:

GET /api/v1/orders?symbol=BTCUSDT&limit=10
ParameterValue
timestamp1696248123
path/api/v1/orders
querysymbol=BTCUSDT&limit=10

Resulting payload = 1696248123/api/v1/orderssymbol=BTCUSDT&limit=10

Signature:

import hmac, hashlib, time

secret_key = "sk_your_secret_key"
timestamp = str(int(time.time()))
path = "/api/v1/orders"
query = "symbol=BTCUSDT&limit=10"

payload = timestamp + path + query
signature = hmac.new(
    secret_key.encode(),
    payload.encode(),
    hashlib.sha256
).hexdigest()
const crypto = require("crypto");

const secretKey = "sk_your_secret_key";
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = "/api/v1/orders";
const query = "symbol=BTCUSDT&limit=10";

const payload = timestamp + path + query;
const signature = crypto
  .createHmac("sha256", secretKey)
  .update(payload)
  .digest("hex");

POST / PUT Requests

Payload formula example:

POST /api/v1/orders
Body: {"symbol":"BTCUSDT","amount":1.5}
ParameterValue
timestamp1696248123
path/api/v1/orders
query(empty)
body_hashSHA256('{"symbol":"BTCUSDT","amount":1.5}')

Resulting payload = 1696248123/api/v1/orders + body_hash

Signature:

import hmac, hashlib, json, time

secret_key = "sk_your_secret_key"
timestamp = str(int(time.time()))
path = "/api/v1/orders"
body = json.dumps({"symbol": "BTCUSDT", "amount": 1.5}, separators=(",", ":"))

body_hash = hashlib.sha256(body.encode()).hexdigest()
payload = timestamp + path + body_hash
signature = hmac.new(
    secret_key.encode(),
    payload.encode(),
    hashlib.sha256
).hexdigest()
const crypto = require("crypto");

const secretKey = "sk_your_secret_key";
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = "/api/v1/orders";
const body = JSON.stringify({ symbol: "BTCUSDT", amount: 1.5 });

const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const payload = timestamp + path + bodyHash;
const signature = crypto
  .createHmac("sha256", secretKey)
  .update(payload)
  .digest("hex");

Exception: POST / PUT multipart/form-data Requests
🚧

Important

  • SHA256(body) is omitted entirely
  • Body content is not included in signature calculation
📘

Reason: multipart request bodies are dynamically generated and cannot be reliably hashed.

Payload formula example:

POST /api/v1/files/upload?type=invoice
Content-Type: multipart/form-data
ParameterValue
timestamp1696248123
path/api/v1/files/upload
querytype=invoice
body_hash(omitted)

Resulting payload = 1696248123/api/v1/files/uploadtype=invoice





Full Request Example

timestamp=$(date +%s)
path="/merchant/invoices"
body='{"amount":100,"currency":"USDT","network":"tron"}'
body_hash=$(echo -n "$body" | sha256sum | cut -d' ' -f1)
payload="${timestamp}${path}${body_hash}"
signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "sk_your_secret_key" | cut -d' ' -f2)

curl -X POST "https://api.asterium.io/crypto-acquiring${path}" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: pk_your_public_key" \
  -H "X-Signature: ${signature}" \
  -H "X-Timestamp: ${timestamp}" \
  -d "$body"
import hmac, hashlib, json, time, requests

SECRET_KEY = "sk_your_secret_key"
PUBLIC_KEY = "pk_your_public_key"
BASE_URL = "https://api.asterium.io/crypto-acquiring"

def make_signed_request(method, path, body=None, query=""):
    timestamp = str(int(time.time()))

    if method in ("POST", "PUT") and body:
        raw_body = json.dumps(body, separators=(",", ":"))
        body_hash = hashlib.sha256(raw_body.encode()).hexdigest()
        payload = timestamp + path + query + body_hash
    else:
        payload = timestamp + path + query

    signature = hmac.new(
        SECRET_KEY.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    headers = {
        "X-API-Key": PUBLIC_KEY,
        "X-Signature": signature,
        "X-Timestamp": timestamp,
        "Content-Type": "application/json",
    }

    url = f"{BASE_URL}{path}"
    if query:
        url += f"?{query}"

    return requests.request(
        method, url, headers=headers,
        data=raw_body if body else None
    )

# Create an invoice
response = make_signed_request(
    "POST",
    "/merchant/invoices",
    body={"amount": 100, "currency": "USDT", "network": "tron"}
)
print(response.json())
const crypto = require("crypto");

const SECRET_KEY = "sk_your_secret_key";
const PUBLIC_KEY = "pk_your_public_key";
const BASE_URL = "https://api.asterium.io/crypto-acquiring";

async function makeSignedRequest(method, path, body = null, query = "") {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  let payload;

  if (["POST", "PUT"].includes(method) && body) {
    const rawBody = JSON.stringify(body);
    const bodyHash = crypto.createHash("sha256").update(rawBody).digest("hex");
    payload = timestamp + path + query + bodyHash;
  } else {
    payload = timestamp + path + query;
  }

  const signature = crypto
    .createHmac("sha256", SECRET_KEY)
    .update(payload)
    .digest("hex");

  const url = `${BASE_URL}${path}${query ? "?" + query : ""}`;

  const response = await fetch(url, {
    method,
    headers: {
      "X-API-Key": PUBLIC_KEY,
      "X-Signature": signature,
      "X-Timestamp": timestamp,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  return response.json();
}

// Create an invoice
(async () => {
  const result = await makeSignedRequest(
    "POST",
    "/merchant/invoices",
    { amount: 100, currency: "USDT", network: "tron" }
  );

  console.log(result);
})();