Authentication
Every /v1/* request carries three headers:
| Header | Value |
|---|---|
x-client-id |
your client id |
x-timestamp |
current unix time in seconds (±300 s of server time) |
x-signature |
hex HMAC-SHA256 of the message below, keyed with your secret |
message = client_id + timestamp + METHOD + path + raw_bodysignature = hex( hmac_sha256( secret, message ) )METHODis uppercase.pathis the URL path only — no host, no query string. URL-encode path segments before signing, exactly as sent.raw_bodyis the exact bytes you send (empty forGET). Serialize JSON once and sign those bytes; re-serializing can reorder keys and break the signature.- The query string is not signed; it is protected by TLS.
Python
Section titled “Python”import hashlib, hmac, json, time, urllib.request
BASE, CID, SECRET = "https://api.oldsteelarsenal.com", "my-client", "…"
def call(method: str, path: str, payload: dict | None = None): body = b"" if payload is None else json.dumps(payload).encode() ts = str(int(time.time())) sig = hmac.new(SECRET.encode(), f"{CID}{ts}{method}{path}".encode() + body, hashlib.sha256).hexdigest() req = urllib.request.Request(BASE + path, data=body or None, method=method, headers={"x-client-id": CID, "x-timestamp": ts, "x-signature": sig, "content-type": "application/json"}) with urllib.request.urlopen(req, timeout=20) as r: return json.load(r)
call("POST", "/v1/ffl/search", {"zip": "76648", "radius_miles": 25})Node.js
Section titled “Node.js”import { createHmac } from 'node:crypto'
const BASE = 'https://api.oldsteelarsenal.com', CID = 'my-client', SECRET = '…'
export async function call(method, path, payload) { const body = payload === undefined ? '' : JSON.stringify(payload) const ts = String(Math.floor(Date.now() / 1000)) const sig = createHmac('sha256', SECRET).update(CID + ts + method + path + body).digest('hex') const r = await fetch(BASE + path, { method, body: body || undefined, headers: { 'x-client-id': CID, 'x-timestamp': ts, 'x-signature': sig, 'content-type': 'application/json' }, }) if (!r.ok) throw new Error(`${r.status} ${await r.text()}`) return r.json()}Key rotation
Section titled “Key rotation”When we rotate your secret, the previous one keeps working for 24 hours so you can deploy the new one without downtime.
