iCryptoNow

Authentication

Every request to /v1/* is signed with HMAC-SHA256 using your API secret. Keep the secret on your server.

Required headers

HeaderValue
X-Key-IdYour API key id, for example ak_3f9c…
X-TimestampCurrent Unix time in milliseconds (13 digits)
X-SignatureHex-encoded HMAC-SHA256 of the string to sign (64 characters)
Content-Typeapplication/json when the request has a body

String to sign

Join four values with a newline (\n):

{X-Timestamp}
{METHOD}
{path including query string}
{raw request body}
  • METHOD is uppercase: GET or POST.
  • Path is exactly what you send, including the query string, for example /v1/quote?chain=tron&token=USDT&direction=in&amount=50000000.
  • Body is the exact JSON bytes you send. For GET, use an empty string (the string to sign then ends with a newline).
  • Key is your API secret exactly as issued, as UTF-8 text. Do not base64-decode it.

Signing a request

import crypto from "node:crypto";

const BASE = process.env.ICN_BASE_URL;  // given to you at onboarding
const KEY_ID = process.env.ICN_KEY_ID;  // ak_...
const SECRET = process.env.ICN_SECRET;  // use the string exactly as issued

export async function icn(method, path, body) {
  const raw = body === undefined ? "" : JSON.stringify(body);
  const ts = Date.now().toString(); // milliseconds
  const signature = crypto
    .createHmac("sha256", SECRET)
    .update(`${ts}\n${method}\n${path}\n${raw}`)
    .digest("hex");

  const res = await fetch(BASE + path, {
    method,
    headers: {
      "X-Key-Id": KEY_ID,
      "X-Timestamp": ts,
      "X-Signature": signature,
      ...(raw && { "Content-Type": "application/json" }),
    },
    body: raw || undefined, // send exactly the bytes you signed
  });
  return { status: res.status, body: await res.json() };
}

Rules

  • The timestamp must be within 5 minutes of our clock. Keep your servers on NTP.
  • Each signature can be used once. To retry, sign again with a new timestamp.
  • Requests are accepted only from the IP addresses (CIDRs) registered for your key.
  • Request bodies are limited to 64 KB.
Every authentication failure returns the same response, so it never reveals which check failed:
401 Unauthorized
{
  "error": { "code": "unauthorized", "message": "authentication failed" }
}

If you get a 401, check in this order: clock drift, the exact path and body you signed, the secret, and your server's outgoing IP.

Chat with us