Authentication
Get a key¶
In studio → API keys, pick a hub and generate a key. You receive:
clientId— public, e.g.0xb_live_ab12…. Goes in thex-api-keyheader.secret— shown once. Store it server-side. Used only to sign writes.
Warning
The secret is displayed a single time and never again. If you lose it, revoke the key and mint a new one. Never expose the secret in browser code.
Reads — public key only¶
Send the clientId in x-api-key:
This is safe to call from a browser (CORS is enabled).
Writes — HMAC signature¶
Write requests (POST /hub/{slug}/bits/earn) additionally require a signature
that proves you hold the secret without sending it.
Signing key = sha256(secret) (hex). This is also what the server stores,
so it can verify without ever seeing the raw secret.
Payload to sign:
Headers to send alongside x-api-key:
| Header | Value |
|---|---|
x-api-timestamp |
Unix seconds. Must be within ±300s of server time. |
x-api-signature |
HMAC-SHA256(key = sha256(secret), payload), hex. |
Reference implementation (Node)¶
import { createHash, createHmac } from "node:crypto";
function signedHeaders({ clientId, secret, method, path, body }) {
const ts = String(Math.floor(Date.now() / 1000));
const signingKey = createHash("sha256").update(secret).digest("hex");
const payload = `${ts}.${method.toUpperCase()}.${path}.${body}`;
const sig = createHmac("sha256", signingKey).update(payload).digest("hex");
return {
"x-api-key": clientId,
"x-api-timestamp": ts,
"x-api-signature": sig,
"content-type": "application/json",
};
}
const path = "/api/v1/hub/aevon/bits/earn";
const body = JSON.stringify({ wallet: "So1...", kind: "engage:post", ref: "post-123" });
await fetch("https://vectorlabz.io" + path, {
method: "POST",
headers: signedHeaders({ clientId, secret, method: "POST", path, body }),
body,
});
Note
The ref field makes an award idempotent — replay the same signed request (or
pass the same ref) and the points are awarded only once. Use it for
end-to-end safety across retries.
Replay & timestamp window¶
A signed request is valid for ±300 seconds. Sign right before you send. If you batch or queue writes, sign each one at send time, not enqueue time.