Production API
HMAC authentication
Exact headers, canonical payload, and stable HMAC-SHA256 test vectors.
On this page
Every endpoint except GET /v1/time requires HMAC-SHA256. The API secret is never transmitted; it signs a canonical request payload.
Required headers
Authorization: HMAC-SHA256 {api_key}:{base64_signature}
X-Request-Timestamp: {unix_seconds}
Requests with a body also require Content-Type: application/json.
Canonical payload
{timestamp}\n{METHOD}\n{path}\n{query}\n{bodyHash}
Rules:
- uppercase
METHOD; pathcontains only the request path, such as/v1/energy/orders;queryis the exact transmitted query string including its leading?, or empty;bodyHashis the lowercase hex SHA-256 of the exact UTF-8 body bytes — and the empty string when the request has no body;- never reserialize JSON after signing it.
A request without a body signs an empty last segment. Do not hash the empty string: SHA-256("") is a fixed non-empty digest (e3b0c442…), so the server builds a different canonical payload than you did and rejects the signature. This applies to every GET and DELETE, and to a POST sent without a body.
So GET /v1/balance signs exactly this — five segments, the last one empty, and the string ends with a newline:
1784376000\nGET\n/v1/balance\n\n
The signature is base64(HMAC-SHA256(api_secret, canonical_payload)).
Node.js implementation
import crypto from 'node:crypto';
export function sign(secret, timestamp, method, path, query = '', body = '') {
const bodyHash = body
? crypto.createHash('sha256').update(Buffer.from(body, 'utf8')).digest('hex')
: '';
const canonical = `${timestamp}\n${method.toUpperCase()}\n${path}\n${query}\n${bodyHash}`;
return crypto.createHmac('sha256', secret).update(canonical, 'utf8').digest('base64');
}
Stable test vectors
Use timestamp 1784376000 and secret rtn_sk_live_0123456789abcdefghijklmnopqrstuvwxyzABCDEFG.
| Request | Expected signature |
|---|---|
GET /v1/balance |
MAcY78Aad0bL9LGIJGMHR4plQ9jZH3TPUrYEJIMnSFY= |
POST /v1/energy/orders with the body below |
4+nngnu7C6PVGfCs3zPJ34PWylIZdkdNSw8wmoywnTk= |
{"client_request_id":"order-1042","address":"TJRabPrwbZy45sbavfcjinPJC18kjpRTv8","energy":65000,"duration_minutes":60,"expected_total_trx":"3.200000"}
If a vector does not match, fix serialization, query handling, or newlines before sending production traffic.
Authentication failures
An invalid scheme, key, signature, timestamp, or IP allowlist returns 401 as Problem Details. Repeated failures can return 429; respect Retry-After.
Rentron