Business

Gifts, codes & referrals

Two growth mechanisms share the panel: gift/redeem codes (src/routes/_authed.gifts.tsx, _authed.redeem-codes.tsx) for one-off promotions, and the referrals program (src/routes/_authed.referrals.tsx) for ongoing invite-a-friend rewards.

Both Coupons (/_authed/gifts, rendered with mode="coupon") and Redeem codes (/_authed/redeem-codes, re-exports the same page with mode="redeem") share one backend table of "gift codes" (api.gifts.list(){ codes, redemptions, adminChatCount }), disambiguated by a kind field ("coupon" | "redeem") with a legacy fallback: codes matching rewardType === "points" and starting with GRVPN- are treated as redeem if kind is missing.

Concept differences

CouponsRedeem codes
DistributionBroadcast as a DM to all bot usersPosted into groups/channels where the bot is admin
Typical reward types% off a plan (plan_discount), free plan (free_plan)Points, free plan, or plan via points (plan_points)
Use limitUp to maxUses total, per codeEach user can redeem a given code only once (server-enforced), even if maxUses allows more total uses across users

Reward types (GiftRewardType)

  • points — flat points credit.
  • free_plan — immediately provisions the chosen plan for the user (optionally plus extra durationDays/durationHours on top of the plan default).
  • plan_points — grants exactly enough points to buy the specified plan at the user's local price.
  • plan_discount — coupon-only; credits the coupon value in points equal to discountPercent% of the plan price; the user pays the remainder.
  • plan_days — "Add days to plan"; defined in REWARD_LABEL but not exposed in the current create dialog (a supported backend type not yet wired into the UI form).
  • prepaid — "Prepaid top-up"; same status as plan_days, defined but not in the active create dialog.

Code fields

FieldNotes
codeAuto-generates as GRVPN-XXXXXX if left blank; 3–40 chars, A-Z0-9_-.
rewardType, planIdWhich reward, and the plan involved (when applicable).
points, discountPercentReward magnitude for points-based and discount-based rewards.
durationDays / durationHoursExtra time added on top of the plan default, for free_plan.
maxUsesTotal redemption cap across all users.
expiresAtOptional expiry date.
cooldownDays0 = use the global cooldown setting (anti-abuse).
noteInternal label, not shown to users.
active, kindOn/off toggle, and coupon vs redeem.
broadcastOptional boolean: fire distribution immediately on creation.

Endpoints (api.gifts)

text
list()                                     codes + redemptions + adminChatCount
chats()                                    bot's chats: { chatId, title, type, isAdmin }
create(payload)                            response includes code, optionally broadcastResult
update(code, { active })                   toggle a code on/off
remove(code)                               delete a code
sendToChat(code, chatId)                   send/post the code to one specific admin chat
broadcast(code, { toChats, toUsers, chatIds?, userIds? })  full broadcast dialog

UI features

  • Stat tiles: total codes, active codes, total redemptions, total points paid out.
  • Table columns: Code (kind badge + copy button), Reward (human summary per reward type), Uses (used/maxUses, plus cooldown), Expires, Note, Active toggle, Actions.
  • Recent redemptions table: timestamp, code, redeemer (links to tg://user?id=), reward summary emoji line.
  • Export CSV — client-side, dumps both codes and redemptions with columns: type, code, reward_type, plan, points, discount_percent, duration_days/hours, used, max_uses, active, expires_at, note, telegram_id, telegram_name, redeemed_at.

Bot-side redemption

In backend/agent/bot.py: /redeem and /coupon are registered commands (cmd_redeem, cmd_coupon) plus inline "🎁 Redeem" / "🎟 Coupon" prompts (user_gift_prompt, user_coupon_prompt) that ask the user to type a code. _handle_gift_redeem then grants points/plan/discount and confirms with a message ending "Tap /start to see your updated balance."

Issuing a redeem code (points, for a Telegram group)

  1. 1
    Add the bot as admin of the target group/channel (required — shown as a live "N chats where bot is admin" count).
  2. 2
    Go to Redeem codes → New code.
  3. 3
    Reward type: Points; set the point amount.
  4. 4
    Set Max uses (total redemptions allowed) and optionally an expiry date / cooldown.
  5. 5
    Optionally toggle Announce to groups & channels at creation, or leave off and send later via the Send icon (specific groups) or the megaphone icon (broadcast to all admin chats).
  6. 6
    Save — the bot posts the code (if broadcasting) with /redeem <code> instructions; each user in the group can redeem once.

Issuing a discount coupon

  1. 1
    Go to Coupons → New code.
  2. 2
    Reward type: % off a plan; pick the plan and discount percentage.
  3. 3
    Set max total redemptions, optional expiry.
  4. 4
    Toggle Broadcast to all bot users (DM) to send immediately, or send later via the broadcast dialog (all users, specific chats, or specific Telegram IDs).
  5. 5
    Users apply it in the bot with /coupon <code> (or the "🎟" menu button) when buying a plan; the discount is applied as points credit at checkout.

Route: /_authed/referrals. Configures and monitors the invite-a-friend rewards program, plus a full history log and top-referrer leaderboard.

Data (api.referrals.overview(limit))

settings
enabled, pointsPerReferrer, pointsPerReferee, pointsOnTrial, pointsPerSignup, pointsPerUnit, maxPointsPerUser, maxReferralsPerUser, pkrPerUsd.
stats
totalUsers, totalReferred, totalReferrers, totalPointsPaid, logEntries.
logs[]
{ id, ts, referrer, referee?, kind, reason, points }; kind is one of purchase | trial | referee-bonus | admin | other, each with a distinct colored badge/icon.
topReferrers[], referredBy
Leaderboard, and a map keyed by referrer telegram id → list of referred users, shown in a dialog when a referrer row is opened.

How attribution works

Referral links use each user's referralCode. get_user_ctx in bot.py accepts a refCode on the /start deep-link and posts it to /bot/user/start, which the backend uses to attribute a new signup to a referrer. Attribution and payout rate enforcement happen server-side, not in the frontend. _referral_howto and user_ref in bot.py render the user-facing referral screen (their link, code, and stats), and _rewards_received_block summarizes rewards received so far.

How rewards and payouts work

  • pointsPerReferrer — points credited to the referrer when their referral converts.
  • pointsPerReferee — points credited to the new user for having been referred.
  • pointsPerSignup — points on plain signup (independent of referral).
  • pointsOnTrial — points credited when a referred user takes a free trial.
  • pointsPerUnit is the base conversion rate: points equal to 1 PKR. Displayed conversion: oneUsdPts = pointsPerUnit * pkrPerUsd (points equal to 1 USD). Example shown in the UI: "300 pts = 1 PKR · 84,000 pts = 1 USD (1 USD ≈ 280 PKR)".
  • maxPointsPerUser (0 = unlimited) caps total referral earnings per user; maxReferralsPerUser (0 = unlimited) caps how many invites count per referrer.
  • All payouts land as points, which are then spendable toward plans exactly like any other points balance (see Wallet & tracks).

pointsOnTrial is not yet wired to Settings API

It is tracked in local UI state on the Referrals page but explicitly not sent by api.settings.save(...) yet — a code comment notes it "isn't exposed via settings API; skip until backend adds it."

Setup — turning on referrals

  1. 1
    Go to Referrals.
  2. 2
    Flip the Program switch to Enabled.
  3. 3
    Set Points per PKR — this defines the whole points economy scale.
  4. 4
    Optionally cap Max points / user and Max referrals / user to control payout risk.
  5. 5
    Click Save settings (calls api.settings.save({ referralEnabled, pointsPerReferrer, pointsPerReferee, pointsPerSignup, pointsPerUnit, maxPointsPerUser, maxReferralsPerUser })).
  6. 6
    Configure the actual per-event payouts (pointsPerReferrer, pointsPerReferee, pointsOnTrial, pointsPerSignup) in the Signup bonus & referral rewards card (BotRewardsEditor).
  7. 7
    Users now see their referral link/code via the bot's referral menu and earn points automatically as referred users sign up, take a trial, or purchase.

Log and referrer tables are searchable by name/id/code; clicking a referrer filters the log to just their referrals, and a dialog lists everyone a specific referrer brought in.