BricqsBricqs

Pattern: sports prediction

A pre-match prediction game across a tournament. Users pick the outcome, predictions lock at kickoff, scoring runs on the final result, the weekly leaderboard awards prizes. Sponsorable on every screen.

Last updatedMay 2026

Key takeaways

Quick read
  • Each match is a contest. Predictions are events with attributes; scoring evaluates after the result is known.
  • Kickoff lock is enforced server-side via window.starts_at. Late predictions return 422.
  • Weekly leaderboard is a separate contest with a 7-day window; it aggregates the per-match scores.
  • Sponsor logos and themes are configured per contest; the SDK reflects them automatically.
  • Edge cases (rain, abandonment, retirement) handled via admin endpoints, not by editing the active contest.

Anatomy

What you are building

API

Contests (per-match and weekly) and their scoring rules are configured in the dashboard. Your app submits predictions as events; scoring and leaderboards run server-side. Match results are recorded from the dashboard, after which the contest scores predictions.

SDK

useEngagement renders the prediction form; useLeaderboard renders the bracket and the global top.

User sees

Live match list with countdown to lock. Pre-match: pick the winner. Post-match: score, rank, points to next position.

Step 1: config

Configure one contest per match

Contests and their scoring rules are configured in the dashboard, not via a public API. For each match you set up a contest with:

text
Per-match contest (dashboard):

  Title        Qualifier Semi-Final, May 3
  Window       2026-05-03 18:00Z  ->  22:00Z
  Lock         at kickoff (predictions close)
  Scoring      prediction event 'prediction_submitted'
                 winner correct   -> 10 pts
                 exact score      -> 25 pts

Predictions arrive as events from your app; the contest scores them
server-side once you record the result (Step 4).

Step 2: aggregate

Weekly leaderboard contest

A second, longer contest aggregates the week’s per-match scores into a leaderboard with a prize structure. Also configured in the dashboard:

text
Weekly leaderboard contest (dashboard):

  Window          2026-05-01  ->  2026-05-07
  Aggregates      the week's per-match contest scores
  Prize structure ranks 1-50  ->  voucher 500

Contest scoring and aggregation run inside the contest engine on background
workers; you do not wire the per-match totals together yourself.

Step 3: predict

Submit a prediction

components/PredictionForm.tsx·tsx
"use client";
import { useEngagement } from "@bricqs/sdk-react";

export function PredictionForm({ matchId }: { matchId: string }) {
  const { engagement, state, submit } = useEngagement({
    engagementId: matchId,
    sync: false,
  });

  async function pick(team: string, score: string) {
    await submit({
      attributes: { winner: team, score },
      attemptId: `prediction:${matchId}:${userId}`,
    });
  }

  if (!engagement) return null;
  if (state.locked) return <p>Locked at kickoff.</p>;

  return (
    <div>
      <button onClick={() => pick("home", "2-1")}>Home wins 2-1</button>
      <button onClick={() => pick("away", "1-0")}>Away wins 1-0</button>
    </div>
  );
}

Step 4: record result

When the match ends

text
Record the match result in the dashboard (Contests -> the match -> result):

  Outcome   winner = home, score = 2-1

What happens next, server-side:
  1. The contest moves into scoring.
  2. Each prediction is scored against the contest's rules.
  3. When scoring completes, the contest.completed.v1 webhook fires and the
     weekly leaderboard reflects the new points.

Step 5: leaderboard

Bracket and global top

components/PredictionLeaderboard.tsx·tsx
"use client";
import { useLeaderboard } from "@bricqs/sdk-react";

export function PredictionLeaderboard() {
  // engagementId inherited from <BricqsProvider>
  const { entries, myRank } = useLeaderboard({
    code: "week_18_leaderboard",
    limit: 10,
  });

  return (
    <div>
      {myRank != null && <p className="mb-3 text-sm text-slate-500">You&apos;re #{myRank}</p>}
      <RankList title="Top 10" rows={entries} myRank={myRank} />
    </div>
  );
}

Step 6: edge cases

Rain, abandonment, walkover

text
Handle these from the dashboard (Contests -> the match):

  Match abandoned  -> void the contest; no points are awarded
  Walkover         -> record the result as a walkover; predictions for the
                      winning side score normally

Voiding and result-recording are dashboard actions. The contest engine then
cancels or re-scores predictions accordingly.

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.