Platform Stamps & Referral
⚠️ Superseded by befday Points Currency. A platform-level "befday card" accruing weighted points from referrals, sign-ups, and merchant-card completion — replaced by a spendable points currency.
Status: ⚠️ Superseded by befday Points Currency
Date: June 2026
Decision: Add a platform-level “befday card” on top of the existing per-merchant stamp cards. It accrues weighted points from multiple sources — referrals, sign-ups, and completing a merchant card — and when the befday card is completed the user unlocks a birthday perk. Referral/sign-up rewards require a verified phone and are idempotent per (referrer, referee).
Superseded by befday Points Currency
befday Points Currency evolves the befday card from a single completion card (fill to a goal → one perk) into a spendable points currency (Setel/Mesra-style) — points are earned proportionally to spend, accumulate as a balance, and are redeemed either via a reward catalog or converted to merchant-funded credit/discount. The referral loop, verified-phone anti-fraud, and idempotency rules below remain valid and carry forward; the completion-card mechanic and the
befday_cards.goal/completed_atshape are replaced by a points-balance + ledger model.
Context
befday has per-merchant stamp cards today: one stamp_card per (store_customer, shop), earning 1 stamp per paid order plus a one-time welcome stamp, completing at a per-shop goal (see Stamps & Customers and the Stamp Identification Flow). These are siloed — finishing a café card does nothing for the user’s relationship with the wider network.
The proposal adds a second tier:
- Merchant stamps — exist today; earned per purchase at one shop.
- befday stamps (new) — a platform-owned card aggregating achievements across the network.
Plus a referral loop: refer a friend → both earn befday progress. Completing any merchant card also feeds the befday card. Completing the befday card grants an incentive.
Decision
Introduce a platform-level befday card driven by a weighted points ledger. Different actions grant different point values; the card completes at a points goal and unlocks a birthday perk. Referral and sign-up rewards count only for phone-verified accounts and are idempotent per referral pair.
Why a ledger, not a flat card
The three reward sources are not the same unit of value or effort:
| Action | Value to befday | Frequency | Naive “1 stamp” problem |
|---|---|---|---|
| Refer a friend (verified) | High (new user) | Rare | Could farm referrals to fill card |
| Sign up | Medium (acquisition) | Once | One-off |
| Complete a merchant card | High (real spend) | Occasional | Could take 50 purchases to matter |
If every action = 1 befday stamp, the economics break (referral-farming, or spend that barely moves the card). The fix: separate “points” from “the card.” A befday_points ledger grants each event_type a tunable points value; the card completes at a points goal. Reuses the stamp_events audit pattern, generalized to the platform.
Point sources (initial weights — tunable config, not hardcoded)
event_type |
Trigger | Points (initial) | Idempotency |
|---|---|---|---|
referral |
Referred friend signs up and verifies phone | 3 | once per (referrer_user_id, referee_user_id) |
signup |
New user registers + verifies phone (uses a referral code) | 1 | once per user_id |
merchant_card_complete |
Any per-merchant stamp_card reaches its goal |
2 | once per stamp_card.id completion |
Weights are store_settings-style config, adjustable without code changes. Start conservative; tune from data.
Incentive
Completing the befday card unlocks a birthday perk / gift — on-brand for befday (the platform is literally about birthdays). Exact perk is an open question; it may be platform-funded or merchant-sponsored.
Anti-fraud (reuse what we built)
Referrals are the #1 abuse target (fake signups, self-referrals). Reuse the verified phone via WhatsApp OTP identity (Stamp Identification Flow):
- Referral & sign-up points only granted to
phone_verified = trueaccounts. referralis idempotent per(referrer, referee)— re-invite / reinstall / re-register grants nothing extra.- Self-referral blocked:
referrer_user_id != referee_user_id, and referee must be a genuinely new account.
Flow
Referral (two-sided):
1. User A shares their referral code / link from `native`
2. User B installs → registers (Google / Apple) → verifies phone (WhatsApp OTP)
3. On B's first verified registration with A's code:
- befday_points += signup_points for B (idempotent per user)
- befday_points += referral_points for A (idempotent per (A, B))
4. Both befday cards update; perk unlocks when goal reached
Merchant card → befday card:
A. User completes a per-merchant stamp_card (reaches its goal)
B. Server grants merchant_card_complete points to that user's befday card
(idempotent per stamp_card.id) — requires the card's store_customer to be
linked to a befday user_id
C. befday card updates; perk unlocks at goal
Data Model Impact (sketch)
Platform-level, keyed by user.id (not store_customer / shop). Mirrors the merchant stamp pattern one tier up.
befday_cards
One platform card per befday user.
| Column | Type | Notes |
|---|---|---|
id |
uuid | PK |
user_id |
text | FK → user.id, unique |
points |
integer | running total |
goal |
integer | points needed to unlock the perk |
completed_at |
timestamp | nullable — set when points >= goal |
updated_at |
timestamp |
befday_point_events
Audit ledger — one row per points-granting action. Generalizes stamp_events to the platform.
| Column | Type | Notes |
|---|---|---|
id |
uuid | PK |
befday_card_id |
uuid | FK → befday_cards.id |
event_type |
enum | referral | signup | merchant_card_complete |
points |
integer | snapshot of weight at time of grant |
referrer_user_id |
text | nullable FK → user.id (for referral) |
referee_user_id |
text | nullable FK → user.id (for referral / signup) |
source_stamp_card_id |
uuid | nullable FK → stamp_cards.id (for merchant_card_complete) |
issued_at |
timestamp |
Idempotency constraints:
referral: unique(referrer_user_id, referee_user_id)signup: unique(referee_user_id)whereevent_type = 'signup'merchant_card_complete: unique(source_stamp_card_id)
referrals (optional, for attribution/analytics)
| Column | Type | Notes |
|---|---|---|
id |
uuid | PK |
referrer_user_id |
text | FK → user.id |
referee_user_id |
text | nullable FK → user.id until joined |
code |
text | the referral code shared |
status |
enum | pending | joined | rewarded |
created_at |
timestamp |
Merchant-card-completion hook
When a per-merchant stamp_card reaches goal, fire a merchant_card_complete event into the owning user’s befday card (idempotent per stamp_card.id). Only fires when the card’s store_customer.user_id is set (linked befday account).
API Impact (sketch)
| Procedure | Status | Notes |
|---|---|---|
consumer.referral.getCode |
New | Returns/creates the signed-in user’s referral code + share link |
consumer.referral.redeem |
New | Applies a referral code at registration (server-validated) |
consumer.befdayCard.get |
New | Returns the user’s befday card (points, goal, completion, perk state) |
consumer.befdayCard.history |
New | Lists befday_point_events for the user |
(internal) grantBefdayPoints |
New | Shared, idempotent point-granting used by referral/signup/card-complete |
Point granting is server-side and idempotent, mirroring stamp issuance. Sign-up/referral grants run only after phone verification.
Consequences
| Type | Consequence |
|---|---|
| Pro | Adds a network-wide retention layer above siloed merchant cards; one action can advance two cards (merchant + befday). |
| Pro | Referral is a proven two-sided viral loop, paid in stamps (cheap, on-brand) rather than cash. |
| Pro | Reuses the existing verified-phone anchor for fraud resistance — a real advantage already built. |
| Pro | Weighted ledger keeps economics tunable; no single action dominates or is farmable. |
| Con | New platform-level tables + a points economy to tune (weights, goal, perk cost). |
| Con | Referral abuse must be actively designed against (verified phone, idempotency, self-referral block). |
| Con | The perk must be defined and costed before launch — it’s the make-or-break of the loop. |
Resolved Decisions
| Question | Decision |
|---|---|
| Two tiers? | Yes — keep per-merchant cards; add a platform befday card |
| befday card unit | Weighted points ledger (not flat 1-stamp-per-action) |
| “Stamp at birthday” | Completing a merchant card feeds points into the befday card |
| Referral reward | Two-sided — referrer + verified referee both earn points |
| Incentive | Birthday perk / gift on befday-card completion |
| Anti-fraud | Phone-verified only; idempotent per (referrer, referee); self-referral blocked |
Open Questions
- Point weights & goal: starting values are guesses — set a goal and weights, then tune from real data.
- Perk definition & funding: what exactly is the birthday perk, and is it platform-funded or merchant-sponsored?
- Perk cadence: one-time on completion, or does the befday card reset/repeat (e.g. annually around the birthday)?
- Merchant-card-complete eligibility: does it count retroactively for cards completed before linking, or only after the befday account is linked?
- Referral cap: is there a max referral reward per user per period to bound abuse and cost?
- Cross-shop welcome stamps: does the existing per-shop welcome stamp interact with befday points at all? (Default: no.)