Skip to content

JavaScript SDK

You don't need a package to use the API — it's plain HTTP. But this ~40-line client wraps the read calls and the HMAC write signing so you don't hand-roll it. Drop it into your project.

Note

Keep the secret on your server. The read methods are browser-safe with just the clientId; only earn() needs the secret and must run server-side.

The client

import { createHash, createHmac } from "node:crypto";

export function VectorLabz({ clientId, secret, baseUrl = "https://vectorlabz.io" }) {
  async function read(path) {
    const res = await fetch(baseUrl + path, { headers: { "x-api-key": clientId } });
    if (!res.ok) throw await res.json();
    return res.json();
  }

  return {
    // ── reads (browser-safe) ──────────────────────────────
    hub: (slug) => read(`/api/v1/hub/${slug}`),
    leaderboard: (slug, limit = 10) => read(`/api/v1/hub/${slug}/leaderboard?limit=${limit}`),
    holder: (slug, wallet) => read(`/api/v1/hub/${slug}/holder/${wallet}`),
    stakeStatus: (slug, wallet) => read(`/api/v1/hub/${slug}/stake-status?wallet=${wallet}`),

    // ── signed write (server only — needs secret) ─────────
    async earn(slug, { wallet, kind, delta, ref }) {
      if (!secret) throw new Error("earn() requires a secret — server-side only");
      const path = `/api/v1/hub/${slug}/bits/earn`;
      const body = JSON.stringify({ wallet, kind, delta, ref });
      const ts = String(Math.floor(Date.now() / 1000));
      const key = createHash("sha256").update(secret).digest("hex");
      const sig = createHmac("sha256", key).update(`${ts}.POST.${path}.${body}`).digest("hex");
      const res = await fetch(baseUrl + path, {
        method: "POST",
        headers: {
          "x-api-key": clientId,
          "x-api-timestamp": ts,
          "x-api-signature": sig,
          "content-type": "application/json",
        },
        body,
      });
      if (!res.ok) throw await res.json();
      return res.json();
    },
  };
}

Usage

const vl = VectorLabz({
  clientId: process.env.VL_CLIENT_ID,
  secret: process.env.VL_SECRET, // omit in browser builds
});

// Read — anywhere
const board = await vl.leaderboard("aevon", 25);
const me = await vl.holder("aevon", wallet);

// Write — server only, idempotent via ref
await vl.earn("aevon", { wallet, kind: "engage:post", ref: `post-${postId}` });

Browser vs. server

  • 🌐 Browser

    Instantiate with clientId only. Use the read methods to render leaderboards, holder badges and stake status. Never ship the secret.

  • 🖥️ Server

    Instantiate with clientId + secret. Call earn() when your backend has verified an action worth points. Always pass a stable ref.