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
| Endpoint | Method | Description |
|---|---|---|
| List API Keys | GET merchant/api-keys | Retrieve all active API keys |
| Create API Key | POST merchant/api-keys | Generate a new pk_ / sk_ key pair |
| Validate Request | POST merchant/api-keys/validate-request | Test your HMAC signature implementation |
| Delete API Key | DELETE merchant/:id | Revoke an API key pair |
RememberThe
secretKey(sk_...) is displayed only once at creation time. Store it immediately in a secure vault or secrets manager.
Important Notes
- ⏱️
X-Timestampmust 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 fromrequest_pathbefore signature calculation.
Authentication Headers
Each request must include the following headers:
| Header | Description | Example |
|---|---|---|
X-API-Key | Your public API key | pk_abc123xyz |
X-Signature | HMAC-SHA256 signature (64-character lowercase hex) | b4f2c3a4d8e9... |
X-Timestamp | Unix timestamp in seconds | 1696248123 |
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
| Method | Formula |
|---|---|
GET | timestamp + path + query |
DELETE | timestamp + path + query |
POST | timestamp + path + query + SHA256(body) |
PUT | timestamp + 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| Parameter | Value |
|---|---|
| timestamp | 1696248123 |
| path | /api/v1/orders |
| query | symbol=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}| Parameter | Value |
|---|---|
| timestamp | 1696248123 |
| path | /api/v1/orders |
| query | (empty) |
| body_hash | SHA256('{"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| Parameter | Value |
|---|---|
| timestamp | 1696248123 |
| path | /api/v1/files/upload |
| query | type=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);
})();
