Documentation

Authentication

All API requests require authentication via the x-api-key header and HMAC-SHA256 signature verification via the x-signature, x-timestamp and x-nonce headers.

Required headers
HeaderValueStatus
x-api-keytq_xxxxxRequired
Content-Typeapplication/jsonRequired
x-signatureHMAC-SHA256 hexRequired
x-timestampUnix ms (e.g. 1708300000000)Required
x-nonceRandom, 16+ characters, new for every request (e.g. openssl rand -hex 16)Required
Signature formula
x-signature = HMAC-SHA256(API_SECRET, METHOD:PATH:timestamp:nonce:bodyHash)
PATH is the path with its query string, exactly as sent (e.g. /agents?status=active). For GET requests, the body is signed as an empty string.
API Secret is only shown once when created. Store it securely.
Timestamp tolerance: ±5 minutes. Requests outside this window are rejected.
401 or 403? 401 means something is missing or stale: no x-api-key, or SIGNATURE_REQUIRED, TIMESTAMP_REQUIRED, TIMESTAMP_EXPIRED, NONCE_REQUIRED, NONCE_INVALID. 403 means it was checked and refused: SIGNATURE_INVALID (a wrong secret, or a different path, query or body than you signed), an unknown, revoked or expired key, or an address outside the key's IP allowlist. The body's error or code names which.
HMAC-signed request example
const crypto = require('crypto');

const API_KEY    = 'tq_abc123';
const API_SECRET = 'YOUR_API_SECRET';
const BASE_URL   = 'https://api.touchque.com';

function signRequest(method, path, body, timestamp, nonce) {
  const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
  const msg = `${method.toUpperCase()}:${path}:${timestamp}:${nonce}:${bodyHash}`;
  return crypto.createHmac('sha256', API_SECRET).update(msg).digest('hex');
}

const body = JSON.stringify({ externalUsername: 'user@company.com' });
const timestamp = Date.now().toString();
const nonce = crypto.randomBytes(16).toString('hex');
const method = 'POST';
const path = '/auth/generate-secret';

const response = await axios.post(`${BASE_URL}${path}`, body, {
  headers: {
    'x-api-key': API_KEY,
    'x-signature': signRequest(method, path, body, timestamp, nonce),
    'x-timestamp': timestamp,
    'x-nonce': nonce,
    'Content-Type': 'application/json',
  }
});