BricqsBricqs
Documentation

Points API

Award and deduct points, query balances, and page through transaction history. Awards trigger automatic tier evaluation; when a participant crosses a threshold the response includes the tier upgrade and any unlocked badges. Expiring credits are consumed FIFO before non-expiring credits.

Auth
All endpoints require an X-API-Key header. Production keys are prefixed bq_live_; sandbox keys bq_test_.
Scopes
Participant key All points endpoints accept any valid API key. There is no separate admin tier for points.
Base URLhttps://YOUR_API_DOMAIN/api/v1/gamify

Endpoint inventory

MethodEndpointPurpose
POST/gamify/points/awardAward points to a participant. Idempotent. Returns new balance, tier upgrade, badges unlocked.
POST/gamify/points/deductDeduct points. Idempotent. Returns 400 if the available balance is insufficient.
POST/gamify/points/award-batchAward up to 100 participants in one request. Idempotent. Per-item failures captured in results.
GET/gamify/participants/{participant_id}/pointsCurrent balance, total earned (lifetime), total spent (lifetime).
GET/gamify/participants/{participant_id}/points/transactionsPaginated ledger entries (awards and deductions) for one participant.
POST/api/v1/gamify/points/award
Participant keyIdempotent

Credit a participant with points. Triggers tier evaluation and badge unlock checks. Safe to retry via the Idempotency-Key header; a second call with the same key returns the original transaction without double-awarding.

Headers
FieldTypeDescription
Idempotency-KeystrOptional. 1 to 255 chars matching ^[A-Za-z0-9_\-:.]{1,255}$. Recommended for every call. Descriptive keys ("order-ord_abc") give clean audit trails.
Request body
FieldTypeDescription
participant_idrequiredstrYour participant identifier. 1 to 255 characters. Auto-creates the participant on first interaction.
amountrequiredintPoints to award. Must be greater than 0 and at most 1,000,000.
reasonOptional[str]Free-form audit note (max 500 chars). Surfaces in transaction history and tier-change fact payloads.
metadataOptional[dict[str, Any]]Custom payload stored alongside the transaction (e.g. { order_id, amount_usd }). Returned by the transactions endpoint.
idempotency_keyOptional[str]Legacy body field (max 255 chars). The Idempotency-Key header is preferred; supplying both with different values returns 400 IDEMPOTENCY_KEY_MISMATCH.
expires_atOptional[datetime]When set, the credit expires at this timestamp. Expiring credits are consumed FIFO before non-expiring credits during deductions.
POST /api/v1/gamify/points/award
X-API-Key: bq_live_xxxxx
Content-Type: application/json
Idempotency-Key: order-ord_abc

{
  "participant_id": "user_123",
  "amount": 100,
  "reason": "Purchase completed",
  "metadata": {
    "order_id": "ord_abc",
    "amount_usd": 49.99
  }
}
Response fields
FieldTypeDescription
transaction_idstrUnique ledger id for this award.
participant_idstrEcho of the credited participant.
amountintPoints awarded.
new_balanceintParticipant's available balance after the award.
tier_upgradeOptional[TierUpgradeInfo]Populated when the award caused a tier change. Shape: { code, name, level, previous_code?, previous_level? }. null when no tier change occurred.
badges_unlockedlist[str]Badge codes unlocked by this award. Empty list when no badges fired. Default: []
{
  "transaction_id": "a1b2c3d4-e5f6-...",
  "participant_id": "user_123",
  "amount": 100,
  "new_balance": 1500,
  "tier_upgrade": {
    "code": "gold",
    "name": "Gold Tier",
    "level": 3,
    "previous_code": "silver",
    "previous_level": 2
  },
  "badges_unlocked": ["first_purchase"]
}
Errors
StatusCodeWhen it fires
400IDEMPOTENCY_KEY_INVALIDIdempotency-Key header is empty, longer than 255 chars, or contains characters outside [A-Za-z0-9_\-:.].
400IDEMPOTENCY_KEY_MISMATCHBoth the Idempotency-Key header AND a body idempotency_key were provided and they differ.
401unauthorizedX-API-Key header missing, invalid, expired, or revoked.
403forbiddenTenant is disabled.
429RATE_LIMIT_PARTICIPANT_EXCEEDEDPer-participant write rate cap exceeded. The Retry-After response header tells you when to retry.
POST/api/v1/gamify/points/deduct
Participant keyIdempotent

Debit a participant's available balance (e.g. reward redemption). FIFO-consumes any expiring credits first. Idempotent via the Idempotency-Key header.

Headers
FieldTypeDescription
Idempotency-KeystrOptional. 1 to 255 chars matching ^[A-Za-z0-9_\-:.]{1,255}$. Recommended for every redemption to make retries safe.
Request body
FieldTypeDescription
participant_idrequiredstrYour participant identifier. 1 to 255 characters.
amountrequiredintPoints to deduct. Must be greater than 0 and at most 1,000,000.
reasonOptional[str]Free-form audit note (max 500 chars).
metadataOptional[dict[str, Any]]Custom payload stored alongside the transaction.
idempotency_keyOptional[str]Legacy body field. Prefer the Idempotency-Key header.
POST /api/v1/gamify/points/deduct
X-API-Key: bq_live_xxxxx
Content-Type: application/json
Idempotency-Key: redemption-rdm_42

{
  "participant_id": "user_123",
  "amount": 50,
  "reason": "Reward redemption"
}
Response fields
FieldTypeDescription
transaction_idstrUnique ledger id for this deduction.
participant_idstrEcho of the debited participant.
amountintPoints deducted.
new_balanceintParticipant's available balance after the deduction.
tier_upgradeOptional[TierUpgradeInfo]Always null on deductions (tiers track lifetime earnings, not available balance).
badges_unlockedlist[str]Always empty on deductions. Default: []
{
  "transaction_id": "b7c8d9e0-...",
  "participant_id": "user_123",
  "amount": 50,
  "new_balance": 1450
}
Errors
StatusCodeWhen it fires
400insufficient_pointsAvailable balance is below the requested amount. Body: { detail: "Insufficient points. Available: X, requested: N" }.
400IDEMPOTENCY_KEY_INVALIDIdempotency-Key header is empty, too long, or contains illegal characters.
400IDEMPOTENCY_KEY_MISMATCHHeader and body idempotency keys disagree.
401unauthorizedX-API-Key header missing or invalid.
403forbiddenTenant is disabled.
429RATE_LIMIT_PARTICIPANT_EXCEEDEDPer-participant write rate cap exceeded.
POST/api/v1/gamify/points/award-batch
Participant keyIdempotent

Award points to up to 100 participants in a single request. Each item is processed independently; an individual failure does not roll back the rest. The endpoint itself never raises on per-item validation errors (failures appear inside results[].error).

Request body
FieldTypeDescription
awardsrequiredlist[BatchAwardItem]1 to 100 award items. Each item has the same shape as POST /points/award minus expires_at.
BatchAwardItem
FieldTypeDescription
participant_idrequiredstr1 to 255 characters.
amountrequiredintGreater than 0, at most 1,000,000.
reasonOptional[str]Max 500 chars.
metadataOptional[dict[str, Any]]Custom payload.
idempotency_keyOptional[str]Per-item idempotency. Recommended when retrying a batch.
POST /api/v1/gamify/points/award-batch
X-API-Key: bq_live_xxxxx
Content-Type: application/json

{
  "awards": [
    { "participant_id": "user_1", "amount": 100, "reason": "Weekly bonus" },
    { "participant_id": "user_2", "amount": 100, "reason": "Weekly bonus" },
    { "participant_id": "user_3", "amount": 50,  "reason": "Referral bonus" }
  ]
}
Response fields
FieldTypeDescription
processedintNumber of items that succeeded.
failedintNumber of items whose results[].error is non-null.
resultslist[BatchAwardResult]One entry per input item, in the same order. Shape: { participant_id, transaction_id?, new_balance?, error? }. On success error is null; on failure transaction_id and new_balance are null and error describes the cause.
{
  "processed": 3,
  "failed": 0,
  "results": [
    { "participant_id": "user_1", "transaction_id": "...", "new_balance": 500, "error": null },
    { "participant_id": "user_2", "transaction_id": "...", "new_balance": 350, "error": null },
    { "participant_id": "user_3", "transaction_id": "...", "new_balance": 200, "error": null }
  ]
}
Errors
StatusCodeWhen it fires
401unauthorizedX-API-Key header missing or invalid. Per-item failures never raise at the request level; they appear in results[].error.
403forbiddenTenant is disabled.
GET/api/v1/gamify/participants/{participant_id}/points
Participant key

Returns the participant's current available balance and lifetime totals. total_earned is never decremented by redemptions (it ranks tier and leaderboard progress); only balance moves on deduct.

Path parameters
FieldTypeDescription
participant_idrequiredstrYour participant identifier. 1 to 255 characters.
GET /api/v1/gamify/participants/user_123/points
X-API-Key: bq_live_xxxxx
Response fields
FieldTypeDescription
participant_idstrEcho of the requested participant id.
balanceintAvailable points (lifetime earned minus lifetime spent, plus any expirations).
total_earnedintLifetime points awarded. Used for tier evaluation and leaderboards. Never decremented.
total_spentintLifetime points deducted.
{
  "participant_id": "user_123",
  "balance": 1500,
  "total_earned": 5000,
  "total_spent": 3500
}
Errors
StatusCodeWhen it fires
401unauthorizedX-API-Key header missing or invalid.
403forbiddenTenant is disabled.
GET/api/v1/gamify/participants/{participant_id}/points/transactions
Participant key

Paginated ledger of all awards and deductions for one participant, newest first.

Path parameters
FieldTypeDescription
participant_idrequiredstrYour participant identifier.
Query parameters
FieldTypeDescription
pageint1-indexed page number. Must be >= 1. Default: 1
page_sizeintItems per page. Must be between 1 and 100. Default: 50
GET /api/v1/gamify/participants/user_123/points/transactions?page=1&page_size=20
X-API-Key: bq_live_xxxxx
Response fields
FieldTypeDescription
participant_idstrEcho of the requested participant id.
transactionslist[PointsTransactionEntry]Page of ledger entries. Each entry: { id, amount, reason?, transaction_type?, created_at }.
totalintTotal transactions across all pages for this participant.
pageintEcho of the requested page.
page_sizeintEcho of the requested page size.
{
  "participant_id": "user_123",
  "transactions": [
    {
      "id": "txn_abc",
      "amount": 100,
      "reason": "Purchase completed",
      "transaction_type": "award",
      "created_at": "2026-02-14T10:00:00Z"
    },
    {
      "id": "txn_xyz",
      "amount": -50,
      "reason": "Reward redemption",
      "transaction_type": "deduct",
      "created_at": "2026-02-13T18:22:00Z"
    }
  ],
  "total": 156,
  "page": 1,
  "page_size": 20
}
Errors
StatusCodeWhen it fires
401unauthorizedX-API-Key header missing or invalid.
403forbiddenTenant is disabled.
422validation_errorpage is below 1, or page_size is outside the 1 to 100 range.
Related
← Back to Gamification API