Skip to main content
Build a referral program where an existing user emails a friend, the friend signs up, and both earn points once the friend makes a first qualifying purchase. Your app sends the emails and owns the referral codes. Scrip records who invited whom, counts each stage, pays both sides, and keeps the accounting.

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_participant left at CREATE
  • An asset linked to that program (POINTS, scale 0, UNLIMITED, LOT)
See the quickstart if you need help with setup.

Create the rule set

Save the returned ID as 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.
If 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.
The rule stores the attribution on the friend and counts the signup for the referrer. The referred tag makes a second signup event for the same friend skip, and the external_id check rejects a user who referred themselves.
Nothing is paid at signup. Paying on the first qualifying purchase keeps throwaway signups from earning anything.

3. Qualify on the first purchase

Purchases arrive as ordinary events, with nothing referral-specific in them:
The rule fires once per referred friend. It pays the friend 500 points and marks them qualified. It also pays the referrer 500 points and adds one to their 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.
The 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:
To notify the referrer when they are paid, subscribe a webhook endpoint to 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’s referral_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:
One second later, Scrip delivers a 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:
Decide where the budget lives. On 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. The referred 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 a LOT-mode asset:
Reversal does not undo tags or counters. The 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 resent referral_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.