English
HMAC Request Signing
Protected APIs require X-API-KEY, X-TIMESTAMP, X-NONCE, and X-SIGNATURE. The signature is Base64-encoded HMAC-SHA256 using the issued HMAC key.
text
METHOD\n\nPATH_WITH_QUERY\n\nTIMESTAMP\n\nNONCE\n\nBODY_JSONNode.js example
js
import { createHmac, randomUUID } from 'node:crypto'
const hmacKey = process.env.WALLET_HMAC_KEY
if (!hmacKey) throw new Error('WALLET_HMAC_KEY is required')
const method = 'GET'
const pathQuery = '/networks?is_active=true&page=1&page_size=20'
const timestamp = String(Date.now())
const nonce = randomUUID()
const bodyJson = ''
const stringToSign = [method, pathQuery, timestamp, nonce, bodyJson].join('\n\n')
const signature = createHmac('sha256', hmacKey)
.update(stringToSign)
.digest('base64')Sign the exact outgoing path, query order, encoding, timestamp, nonce, and serialized body. Generate new authentication values for every retry.
For GET and DELETE requests with an empty body, the \n\n separator between the nonce and the empty body remains part of the signature. Shell command substitution ($(...)) removes trailing newlines, so pipe printf output directly into the HMAC calculation instead of storing the canonical string in an intermediate variable. Serialize JSON without formatting whitespace using JSON.stringify or jq -c, then sign and send that string.
| Error | Corrective action |
|---|---|
MISSING_AUTH_HEADER | Add the reported header |
INVALID_API_KEY | Replace or verify the environment key |
INVALID_TIMESTAMP | Synchronize system time |
NONCE_REPLAYED | Generate a new nonce |
INVALID_SIGNATURE | Rebuild the canonical string and signature |
Never log the HMAC key or the complete authentication context.
POST body example
js
const bodyJson = JSON.stringify({
uid: 'user-001', account_type: 'USER', metadata: { source: 'onboarding' }
})
const canonical = ['POST', '/accounts', timestamp, nonce, bodyJson].join('\n\n')Pass bodyJson itself to the HTTP client. Do not let the client serialize the object a second time.
Test GET without body, GET with ordered query parameters, POST with nested JSON, Unicode metadata, an empty object, and a retry with new authentication fields. Use deterministic fixture keys only in tests and never copy production key material into a signing test.