Pattern: referral loop
Give-200, get-200 double-sided referral. Code generated server-side, share message pre-filled, attribution on conversion (not click), per-inviter cap, anti-fraud checks. Always-on, not campaign-bound.
Key takeaways
Quick read- Configure the referral program once. Server generates a unique code per participant.
- Pre-filled WhatsApp share doubles share rate vs free-text.
- Attribution fires on the converted action (signup, first purchase) not on click.
- Per-inviter cap (e.g. 25 successful referrals per quarter) protects the budget.
- Anti-fraud: same device, same payment method, disposable email patterns are blocked server-side.
Anatomy
What you are building
API
POST /admin/referral-programs configures the program. Codes generate on first useReferral call. POST /events submits the converted event with referral_code.
SDK
useReferral returns code, shareUrl, defaultMessage, history, and share().
User sees
Their unique code, a share button that opens WhatsApp/SMS with the message pre-filled, a list of converted invites with reward status.
Step 1: config
Define the program
curl -X POST https://api.bricqs.co/api/v1/admin/referral-programs \
-d '{
"id": "double_sided_q4",
"structure": "double_sided",
"inviter_reward": { "type": "voucher", "value": 200 },
"invitee_reward": { "type": "voucher", "value": 200 },
"trigger_event": "first_purchase_completed",
"caps": {
"per_inviter_per_quarter": 25,
"per_referred_per_lifetime": 1
},
"fraud": {
"block_same_device": true,
"block_same_payment_method": true,
"block_disposable_emails": true
},
"default_message": "Hi! I have been using {brand}. Sign up with my code {code} and we both get 200 INR off."
}'Step 2: render
Code, share, and history
"use client";
import { useReferral } from "@bricqs/headless-react";
export function ReferralPanel() {
const { code, shareUrl, defaultMessage, history, share } = useReferral();
return (
<section>
<p>Your code: <code>{code}</code></p>
<button onClick={() => share({ message: defaultMessage })}>
Share via WhatsApp
</button>
<ul>
{history.map((h) => (
<li key={h.id}>
{h.invitedDisplayName ?? "Pending"} ·{" "}
{h.status === "converted" ? `Earned ${h.rewardLabel}` : h.status}
</li>
))}
</ul>
</section>
);
}Step 3: capture
The invitee path
async function handleSignup(email: string, refCode?: string) {
const user = await createUser(email);
// Tell Bricqs about the signup with the referral context.
// The server will record the link; reward issuance fires on the trigger_event.
await emitToBricqs(
user.id,
"user_signup",
{ referral_code: refCode },
`signup:${user.id}`
);
return user;
}
// Later, when the user makes their first purchase:
async function handleFirstPurchase(userId: string, orderId: string) {
await emitToBricqs(
userId,
"first_purchase_completed",
{ order_id: orderId },
`first_purchase:${userId}`
);
// Bricqs auto-issues the inviter and invitee rewards if all checks pass.
}Step 4: attribution
What runs server-side
When the trigger_event fires for a participant who has a referral_code:
1. Resolve referral_code to inviter participant.
2. Run cap checks (inviter quota, invitee quota).
3. Run fraud checks (same device, same payment method, disposable email).
4. If all pass:
- Issue inviter_reward (idempotent on inviter + invitee + program)
- Issue invitee_reward (idempotent on invitee + program)
- Update referral history.
- Fire reward.issued webhooks.
5. If any fails: log failure, do not issue, fire fraud webhook for review.
You do not call any endpoint to trigger this. The rules engine handles it.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.
