What you’ll build
The result is one rule set with three rules and one monthly budget.
Who does what
Scrip does not send email or generate codes. Referral links are part of your product.
Assumptions
This example assumes you already have:- A program created, with
on_unknown_participantleft atCREATE - An asset linked to that program (
POINTS, scale 0,UNLIMITED,LOT)
Create the rule set
REFERRALS_RULE_SET_ID. Create the three rules below in the order shown. Scrip numbers them as they are added.
1. Count the invite
When a user sends an invite, your app sends an event for the referrer. The idempotency key is the referral code, so a resent email does not count twice.user_123 is not yet a participant, this event enrolls them.
2. Record the signup
When the friend signs up through the link, your app resolves the code to the referrer and sends an event for the friend. The friend does not need to exist yet; this event enrolls them.referred tag makes a second signup event for the same friend skip, and the external_id check rejects a user who referred themselves.
3. Qualify on the first purchase
Purchases arrive as ordinary events, with nothing referral-specific in them:referrals_paid count, finding them through the referred_by attribute saved at signup. The budget caps referral payouts for both sides together at 500,000 points a month.
target reads referred_by from the friend. The condition requires the referred tag, and record_signup always sets that tag and the attribute together, so the attribute is there whenever this rule fires. Without that check, a friend with no referred_by would fail the whole purchase event. See Dynamic targets.
When the monthly budget runs out, the rule still matches but none of its actions run, and Scrip records the skip with reason BUDGET_EXCEEDED. Neither side is paid and the friend is not marked qualified, so their next qualifying purchase after the budget resets pays both sides.
Show progress to the referrer
Your app reads the referrer’s counters to render a referral page:balance.credited. The payload carries the description from the rule, so your handler can tell a referral bonus from other credits.
Edge cases and watchouts
Self-referral
record_signup requires event.referrer_id != participant.external_id, so a user who signs up with their own code is not marked as referred and never qualifies. Reject the code in your app as well so the user sees an error instead of a silent no-op.
The referrer is not in the program
Actions that target the referrer need the referrer to be enrolled and not closed. Otherwise the whole event fails. The referrer’sreferral_invited event enrolls them, so record_signup fails only if your app skips step 1 or the referrer’s account is closed.
If the referrer is closed before the friend’s first purchase, the purchase event fails and the friend is not paid either. Check the referrer’s status before sending the signup event.
Limits per referrer
qualify_referral runs on the friend’s purchase event, so its condition sees the friend’s data, not the referrer’s counters. The simplest limit lives in your app: read invites_sent or referrals_paid before issuing a new code.
To enforce the limit in Scrip, send the referrer’s payout as a separate event addressed to the referrer. In qualify_referral, replace the referrer’s CREDIT and COUNTER with this SCHEDULE_EVENT:
referral_payout event to the referrer. A fourth rule runs on that event, so its condition can read the referrer’s own referrals_paid count. This one pays each referrer for at most 10 referrals:
qualify_referral it now counts only the friend’s bonus, and running out also stops the referrer’s payout from being scheduled. On pay_referrer it caps referrer payouts only.
With this setup the referrer is paid by a separate event, so a refund means reversing two events: the friend’s purchase and the referrer’s referral_payout.
The friend never buys
Nothing is paid and nothing needs cleaning up. Thereferred tag and referred_by attribute stay on the friend so a later purchase can still qualify. To add a deadline, add duration_days(now - participant.enrolled_at) <= 30.0 to the qualify_referral condition.
The qualifying purchase is refunded
The purchase event paid both sides, so reversing it takes back the friend’s 500 points and the referrer’s 500 points. Reversal takes points back from the lots (individually tracked credits) each award created, which is why the setup above uses aLOT-mode asset:
referral_qualified tag stays on the friend, so a later purchase does not pay either side again. The referrer’s referrals_paid count also stays as it is. The reversal response lists both changes, so you can send a compensating event if your policy requires it. See Reversing an event.
Replayed events
Every event in this flow uses an idempotency key built from a stable ID, such as the referral code or the order ID. A resentreferral_signup returns the original event without reprocessing, and the tags on the friend make record_signup and qualify_referral skip on any event that does reach the rules twice.
Next steps
Targeting
Credit a participant other than the one who sent the event.
Campaigns
Double the referral bonus for a month without touching these rules.
Webhooks
Tell the referrer when they have been paid.