Authentication
Bricqs has two credential types with a hard boundary between them: long-lived API keys that live only on your server, and short-lived participant tokens (JWTs) that are the only credential allowed in a browser or mobile app. The SDK enforces the boundary: it refuses to run with a bq_live_ or bq_test_ key client-side.
X-API-Key header. Production keys are prefixed bq_live_; sandbox keys bq_test_.https://api.bricqs.co/api/v1/gamifyCredential types
| Credential | Header | Lives | Default scopes |
|---|---|---|---|
API key (bq_live_ / bq_test_) | X-API-Key | Server only. Never in a bundle, never in a browser. | events:write, gamify:read |
| Participant token (JWT) | Authorization: Bearer | Browser / mobile. Minted server-side, expires in minutes. | read:state, write:events |
The bq_test_ prefix resolves to your isolated test sibling tenant; bq_live_ to production data. Same API, fully separated data.
API-key scopes
Scopes are chosen when you mint the key. A default-minted key covers the standard integration loop (emit events, read state). Value movement is always an explicit grant.
| Scope | Grants | In default? |
|---|---|---|
events:write | Emit events (POST /gamify/events and batch), generate referral codes. | Yes |
gamify:read | Read any participant’s state, points, badges, tier, streaks, rewards, leaderboards, plan. | Yes |
gamify:write | Value movement: award/deduct points, assign tiers, award badges, record streaks, claim rewards, create/update participants. | No, grant explicitly |
admin / gamify:admin | Everything, plus webhook destination management and participant-token minting. | No, grant explicitly |
/api/v1/auth/participant-tokenMint a short-lived participant JWT. Requires an ADMIN-scoped API key (a default key cannot mint tokens). Call it from your backend, never from the browser, and hand only the returned token to the client.
| Field | Type | Description |
|---|---|---|
| participant_idrequired | str | Your stable user identifier. Upserts the participant profile on mint. |
| display_name | Optional[str] | Shown on leaderboards and feeds. |
| Optional[str] | Stored on the participant profile. | |
| external_id | Optional[str] | Secondary identifier from your system. |
| attributes | Optional[dict] | Custom metadata merged into the profile. |
| expires_in_minutes | int | Token TTL. Clamped server-side to 1..60. Default: 5 |
| scopes | Optional[list[str]] | Token scopes. Omit for the default. Default: ["read:state", "write:events"] |
// app/api/bricqs-token/route.ts (Next.js Route Handler)
import { mintParticipantToken } from '@bricqs/sdk-server';
export async function GET() {
const { token, expiresAt } = await mintParticipantToken({
adminApiKey: process.env.BRICQS_ADMIN_API_KEY!, // admin-scoped, server env only
participantId: 'user_123', // your stable user ID
expiresInMinutes: 30,
});
return Response.json({ token, expiresAt });
}| Field | Type | Description |
|---|---|---|
| token | str | The signed participant JWT. Pass to BricqsProvider as participantToken (or return it from a getToken callback). |
| participant_id | str | Echo of the minted identity. |
| tenant_id | str | Your tenant id. |
| expires_at | datetime | Absolute expiry. Re-mint before this; there is no refresh token. |
# The key is a standard bq_live_... key; what makes it "admin" is the
# admin (or gamify:admin) scope granted when it was created.
curl -X POST https://api.bricqs.co/api/v1/auth/participant-token \
-H "X-API-Key: bq_live_..." \
-H "Content-Type: application/json" \
-d '{ "participant_id": "user_123", "expires_in_minutes": 30 }'Troubleshooting: 401 vs 403
401 means the credential itself is missing or invalid. 403 means the credential is valid but not allowed to do this. Every error body carries the standard envelope with a stable error.code and a request_id for support.
| Status | Code | When it fires |
|---|---|---|
| 401 | AUTH_UNAUTHENTICATED | No credential supplied, or the participant JWT is expired. Re-mint the token. |
| 401 | AUTH_API_KEY_INVALID | The X-API-Key value is malformed, revoked, or belongs to no tenant. |
| 403 | AUTH_FORBIDDEN | API key lacks the required scope for this route (gamify:write on value movement is the most common miss), OR a participant token requested another participant's data (pin mismatch). |
| 403 | STREAM_PARTICIPANT_MISMATCH | SSE stream opened for a participant_id that does not match the token subject. |
| 403 | STREAM_SCOPE_MISSING | SSE stream opened with a token minted without read:state. |
Rules that keep you safe
- API keys are server environment variables. Never
NEXT_PUBLIC_, never in source, never in a mobile binary. - The browser gets a participant token only. The SDK rejects
bq_live_/bq_test_values client-side by prefix, but do not rely on that as your only defense. - Mint tokens with the shortest TTL your UX tolerates (default 5 minutes, max 60) and refresh via a
getTokencallback. - Mint keys with the fewest scopes the service needs. One key per service makes rotation a per-service operation; keys show
last_used_atin the dashboard so you can confirm cutover before revoking. - Value movement (
gamify:write) belongs to backend services only; no integration path moves value from a browser.
