Alumni — Marketplace#

Curated deals (coupons + partner-site links) from vetted partners, scoped to the BITS community. Phase 2 launches with ~20 seeded deals across categories.

Who this is for: alumni (and verified students/faculty/staff for deals scoped to the wider institute). Deal visibility honours each deal’s audience_scope, so you only see deals targeted at audiences you belong to.

Where to find it#

  • Deal grid: /app/marketplace.
  • A deal card opens a modal in-place; deals are not on their own URL today.

Browsing#

The grid surfaces:

  • A Deals for you rail at the top, populated by the Stage 19 recommender (signals: deal views, redemption history, chapter affinity).
  • A category chip filter (e.g. food, travel, gear, services — taken from each deal’s categories text[]).
  • An audience filter that resolves to your Principal server-side; you cannot view deals you would not normally be eligible for.

Each card shows partner name + logo, the deal title and short description, the category chip, and the validity window.

Audience scoping#

A deal carries one of:

audience_scopeVisible to
publicany logged-in user
instituteverified institute users (any role)
alumnirole = alumni
batchalumni whose Profile.batch_year == audience_scope_value
chaptermembers of the named chapter group

Scoping is enforced server-side (Service.canSeeDeal in backend/internal/marketplace/service.go). You cannot bypass it via the URL.

Redeeming#

Two deal kinds (from backend/internal/marketplace/types.go):

  • coupon — clicking Get coupon opens the modal, calls RecordRedemption, and returns a coupon code your clipboard receives. The redemption row is logged (source = copy_code).
  • link — clicking Open partner site redirects you outbound after recording the redemption (source = outbound_click).

Either way the redemption flow:

  1. Validates state — only active deals can be redeemed; redeeming a paused, draft, or expired deal returns 422 (ErrDealNotActive).
  2. Validates the validity window — outside [valid_from, valid_until) you get 410 Gone semantics (ErrDealOutsideWindow).
  3. Checks the cap — if the deal has a redemption_cap and the count is at the cap, you get 429 (ErrRedemptionCapReached).
  4. Checks idempotency — for redemption_cap=1 deals, a second redeem returns 409 AlreadyExists with the original redeemed_at timestamp embedded in the message so the UI can render “you redeemed this on Jan 12 at 14:32”.

Reading the cap#

If a deal advertises a cap (e.g. “first 100 redemptions”), the card shows a remaining-count chip. The decrement is row-locked at the database level — no two clients can over-redeem a cap=100 deal simultaneously.

Common issues#

  • “I redeemed a coupon code but it doesn’t work at the partner site” — Bits records a redemption regardless of whether the partner accepts the code. If the partner site rejects the code, contact the partner first; if you suspect a Bits-side issue, message the Marketplace admin via the listed contact email on the partner card.
  • “A deal disappeared mid-flow” — the expiry job runs periodically (marketplace.jobs.go) and flips deals whose valid_until has lapsed. If you opened a card that was on the cusp of expiry, by the time you click redeem the deal may have flipped to expired and the call returns 410.
  • “I can’t see a deal someone else can” — audience scoping. Check which audience_scope the deal carries; if it’s chapter or batch you have to be in scope.
  • “I lost my coupon code”RecordRedemption is idempotent on cap=1 deals, so re-clicking surfaces the original code. For multi-redemption deals, your DealRedemption history is on the deal modal under Your redemptions.