BricqsBricqs
Documentation

Client Events & Callbacks

When an embedded engagement runs (script tag, iframe, or the managed BricqsEngagement component), the runtime notifies your page about what happened via postMessage. This page is the complete, exact list of those client-side messages and how to consume them on each integration path.

Two event vocabularies, do not mix them. Colon names (points:awarded) are CLIENT callbacks emitted by the embed runtime into your page, documented here. Dot names (points.awarded.v1) are SERVER events delivered to your backend via webhooks and the SSE stream, published in the machine-readable event catalog. They are different surfaces with different payloads.

How callbacks reach you

IntegrationMechanismSyntax
Script tagGlobal window.Bricqs (capital B) with short alias namesBricqs.on('points', handler)
Raw iframepostMessage: { source: 'bricqs', type, payload }window.addEventListener('message', ...)
React SDK (managed)Callback props on BricqsEngagement<BricqsEngagement onPointsAwarded={...} />
Headless hooksNo postMessage involved: hooks return data and refresh; live updates via useBricqsStream (server events)usePoints(...)

Message reference

This is the complete emission surface. Every message has the shape { source: 'bricqs', type: '<type>', payload: {...} } and is posted only to your page’s origin (never *). Payload fields below are exhaustive; the embellished fields in older docs (session ids, rarity, scores) do not exist on these messages.

postMessage typeScript-tag aliasReact propPayload
engagement:readyreadyonReady{ engagementId }
activity:completedactivity:completeonActivityComplete{ activityId, activityType, result?, actionResults? }
points:awardedpointsonPointsAwarded{ points, newBalance }
tier:changedtieronTierChanged{ tierCode, tierName, tierLevel }
badge:unlockedbadgeonBadgeUnlocked{ badgeCode, badgeName? }
reward:claimedrewardonRewardClaimed{ rewardName, rewardType, codeValue? }
engagement:resizeinternalinternal{ height } (auto-height plumbing)
Defined but not emitted today: engagement:completed (script-tag alias complete, React prop onComplete) is wired on the consumer side but the runtime does not currently emit it. Do not build a flow that depends on it; derive completion from activity:completed events or read state server-side. This note will be removed if/when the emitter ships. Separately, onEligibilityCheck IS live: it fires with the eligibility result when you pass checkEligibility to BricqsEngagement (it is driven by the eligibility API, not by a postMessage event).

Script tag: window.Bricqs

The embed script exposes window.Bricqs (capital B) with .on(event, handler) and .off(event, handler). It accepts the SHORT alias names from the table above, not the colon postMessage types.

Bricqs.on('points', ({ points, newBalance }) => {
  toast('+' + points + ' points, balance ' + newBalance);
});

Bricqs.on('badge', ({ badgeCode, badgeName }) => {
  toast('Badge unlocked: ' + (badgeName || badgeCode));
});

Bricqs.on('activity:complete', ({ activityType, actionResults }) => {
  analytics.track('bricqs_activity_completed', { activityType });
});

Bricqs.on('reward', ({ rewardName, codeValue }) => {
  if (codeValue) showCouponModal(rewardName, codeValue);
});

Bricqs.on('tier', ({ tierName, tierLevel }) => {
  toast('Welcome to ' + tierName);
});

Raw iframe: postMessage listener

window.addEventListener('message', (event) => {
  const data = event.data;
  if (!data || data.source !== 'bricqs') return;

  switch (data.type) {
    case 'points:awarded':
      updatePointsWidget(data.payload.newBalance);
      break;
    case 'badge:unlocked':
      celebrate(data.payload.badgeCode);
      break;
    case 'activity:completed':
      analytics.track('bricqs_activity', data.payload);
      break;
  }
});

Messages are targeted at your page’s origin. There is no bricqs:<event> concatenated naming scheme; source and type are separate fields.

React SDK: callback props

import { BricqsEngagement } from '@bricqs/sdk-react';

<BricqsEngagement
  id="YOUR_ENGAGEMENT_UUID"
  onReady={() => setLoaded(true)} // onReady receives no arguments
  onActivityComplete={({ activityType, actionResults }) =>
    analytics.track('bricqs_activity_completed', { activityType })}
  onPointsAwarded={({ points, newBalance }) =>
    toast(`+${points} points`)}
  onBadgeUnlocked={({ badgeCode, badgeName }) =>
    toast(`Badge: ${badgeName ?? badgeCode}`)}
  onTierChanged={({ tierName }) => confettiFor(tierName)}
  onRewardClaimed={({ rewardName, codeValue }) =>
    codeValue && showCouponModal(rewardName, codeValue)}
/>

Headless hooks do not use this surface at all: they fetch and refresh data directly, and live server events arrive via useBricqsStream (see the Headless SDK pages).

Next Steps