BricqsBricqs

Headless SDK setup

Install the SDK, wrap your app in the provider, mint a participant token, and start rendering points, tier, and challenges from real data. Five minutes from npm install to first hook.

Reading time7 minutes
Last updatedMay 2026

Key takeaways

Quick read
  • Wrap the app in BricqsProvider once. Every hook reads from this context.
  • Mint participant tokens server-side. Never put admin keys in client code.
  • Pass the env explicitly. Mixing test and live keys is the most common foot-gun.
  • All hooks ship loading and error states. Render skeletons; do not block on them.
  • Use the same provider for SSR. The SDK hydrates safely.

Install

Add the package

bash
npm install @bricqs/headless-react

# or
pnpm add @bricqs/headless-react
yarn add @bricqs/headless-react

Provider

Wrap your app once

The provider sits at the root of your tree. All hooks read from it.

app/providers.tsx (Next.js App Router)·tsx
"use client";
import { BricqsProvider } from "@bricqs/headless-react";

export function Providers({
  children,
  participantToken,
}: {
  children: React.ReactNode;
  participantToken: string;
}) {
  return (
    <BricqsProvider
      tenantId={process.env.NEXT_PUBLIC_BRICQS_TENANT!}
      participantToken={participantToken}
      env={process.env.NEXT_PUBLIC_BRICQS_ENV as "production" | "test"}
      // optional: override the API base URL for self-hosted deployments
      // apiBaseUrl="https://your-bricqs-instance.com"
    >
      {children}
    </BricqsProvider>
  );
}

Auth

Mint participant tokens server-side

The participant token is the only thing the client sees. It expires; refresh from the server.

app/api/bricqs/token/route.ts (Next.js Route Handler)·ts
import { cookies } from "next/headers";

export async function POST() {
  const userId = await getCurrentUserId(); // your auth
  if (!userId) return new Response("Unauthorized", { status: 401 });

  // Trade your server-side admin key for a short-lived participant token.
  const res = await fetch("https://api.bricqs.co/api/v1/auth/participant-token", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.BRICQS_ADMIN_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ participant_id: userId, ttl_seconds: 3600 }),
  });

  const { token } = await res.json();
  cookies().set("bq_token", token, {
    httpOnly: true,
    sameSite: "lax",
    maxAge: 3600,
    path: "/",
  });

  return new Response(JSON.stringify({ ok: true }));
}

Never expose the admin key to the browser. The participant token is scoped to one user and expires inside an hour.

Environments

Live and test keys

Test environment

Test keys (bq_test_...) and a separate tenant. Free to mutate. Most teams use this for staging and CI.

Production environment

Live keys (bq_live_...). Real points, real rewards, real liability. Restrict admin keys to the smallest possible team.

.env.local·bash
# Public (browser-visible). Tenant ID is safe to expose.
NEXT_PUBLIC_BRICQS_TENANT=tenant_brand_xyz
NEXT_PUBLIC_BRICQS_ENV=production

# Server-only. Never prefix with NEXT_PUBLIC_.
BRICQS_ADMIN_KEY=bq_live_aV7p...

First hook

Render points in 10 lines

components/PointsBadge.tsx·tsx
"use client";
import { usePoints } from "@bricqs/headless-react";

export function PointsBadge() {
  const { available, lifetime, isLoading, error } = usePoints();

  if (error) return <span className="text-red-500">Could not load points</span>;
  if (isLoading) return <span className="opacity-50">Loading...</span>;

  return (
    <span className="inline-flex items-center gap-2">
      <strong>{available.toLocaleString()}</strong> pts
      <small className="text-slate-500">
        (lifetime {lifetime.toLocaleString()})
      </small>
    </span>
  );
}

SSR notes

Server components and hydration

The SDK is client-only. Use it inside Client Components. The provider safely hydrates so initial render is consistent.

app/layout.tsx·tsx
import { Providers } from "./providers";
import { getParticipantToken } from "@/lib/auth";

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const token = await getParticipantToken(); // server-side
  return (
    <html lang="en">
      <body>
        <Providers participantToken={token}>{children}</Providers>
      </body>
    </html>
  );
}

The token is fetched server-side and passed in as a prop. The Provider is the only client boundary; everything else can stay server-rendered.

Common mistakes

What goes wrong on first integration

01Mistake

Putting the admin key in client env (NEXT_PUBLIC_BRICQS_KEY).

Fix

Admin keys are server-only. Mint short-lived participant tokens in a server route and pass them to the Provider.

02Mistake

Mixing test and live keys across environments.

Fix

Use a separate tenant for test. Keep BRICQS_ENV in .env and assert it on every server boot.

03Mistake

Calling hooks outside the Provider.

Fix

Wrap the entire authenticated app in BricqsProvider. Hooks throw if no provider is found.

04Mistake

Blocking the entire page on isLoading.

Fix

Render skeletons for points, tier, badges. The hooks return data within 200 to 400ms; UX is better with a placeholder than a spinner.

05Mistake

Forgetting to refresh the participant token before it expires.

Fix

Call your /api/bricqs/token route on app focus or via setInterval at 80 percent of TTL. The Provider exposes a refreshToken hook for this.

Developer FAQ

Common questions when integrating gamification with Bricqs.

Ready to ship?

Wire it up with the Bricqs SDK or API

Headless SDK for React UIs, REST API for any backend. Same engine behind both.

1 brief to align the room2 mechanics max in version one
What happens next
01
Pick the mechanic
Choose the smallest working system for the brief.
02
Launch without rebuilds
Configure rules and rewards in one place.