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{sha256_hex(body)}
Rules:
- uppercase
METHOD; pathcontains only the request path, such as/v1/energy/orders;queryis the exact transmitted query string including its leading?, or empty;- the body hash is lowercase SHA-256 of the exact UTF-8 body bytes, or empty when there is no body;
- never reserialize JSON after signing it.
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 |
8yz9fx4ShXCP/yYoVeu5L2veRg4JrTFixB+2ZYMyEQI= |
{"client_request_id":"order-1042","address":"TXYZ","energy":65000,"duration_hours":1,"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