Referrals and inviter bonuses

Reward members for inviting friends, reward the friends who join, and share a cut of what friends earn with the person who invited them.

A referral program rewards members for bringing friends who go on to take part. For example:

  • "Invite a friend: you both get points" once the friend finishes the welcome quest.
  • "Bring five friends, get a 250-point bonus."
  • "Earn 10% of what your friends earn for 90 days", up to a cap.

Domino keeps track of who invited whom, decides when a friend counts, and pays both sides. Your app shows the invitation link and the results.

For the model behind this guide, read Referral programs. See Compose a campaign for a complete example.

What members see

  • Their invitation link. Each member has a code your app puts in a link they share.
  • A friend joins through the link. The friend opens it and signs in. The first invitation a friend accepts is the one that counts.
  • Rewards once the friend qualifies. When the friend does what the program asks, such as finishing a quest, the inviter and the friend get their rewards.
  • Their progress. The inviter sees how many friends joined and qualified, their progress toward milestones, and their payouts.
  • Privacy for friends. An inviter never sees their friends' identities, balances or activity.

What you can do in Console

The program itself is defined in code. Once it's published, Console Referrals shows, for the selected project and environment, its programs, accepted invitations, who qualified, payouts, point corrections, and rewards that need review. You can:

  • Correct a referral with a reason: suspend or resume payouts, qualify a friend or revoke their qualification, and clear a reward held for review. See Correct, suspend, and resume.
  • Manage inviter groups in Groups, for programs only some inviters earn from.
  • Get started with a coding agent. Until a program is published, the page offers ready-made requests, such as rewarding both sides or adding milestones. Choose Copy prompt and paste it into your coding agent; the prompt includes the project, environment and setup steps.

Publish a referral program

Domino records one inviter per member in each project and environment.

Define qualification and rewards

import { defineReferral, points, questCompleted } from "@domino-sdk/relay/authoring";
import { community } from "../../ledgers";
import welcome from "../../quests/welcome.quest";
import weeklyActivity from "../../quests/weekly-activity.quest";

export default defineReferral({
  key: "friends",
  title: "Bring your friends",
  qualification: questCompleted(welcome),
  rewards: {
    inviter: [points(community, 25)],
    invitee: [points(community, 10)],
  },
  milestones: [
    { key: "five-friends", count: 5, rewards: [points(community, 250)] },
  ],
  bonuses: [
    {
      key: "ongoing",
      ledger: community,
      basisPoints: 1000,
      quests: [welcome, weeklyActivity],
      durationDays: 90,
      maxPoints: 1000,
    },
  ],
});

This program pays the inviter 25 points and the friend 10 once the friend finishes the welcome quest, 250 more at five friends, and 10% of what each friend earns from two quests for 90 days, up to 1,000 points. See Choose eligible activity for each field.

Add the module to your project

Add the module to your existing relay.json:

{
  "referrals": [{ "entry": "src/referrals/friends.ts" }]
}

Preview and publish to test

domino deploy --preview
domino deploy --environment test

The first command shows the proposed definitions. The second publishes them to test.

Live publication

Publish to test directly with domino deploy --environment test. For live, the CLI prepares a request, and a person with publish permission approves it in Console. See reviewed live publication.

Choose eligible activity

Qualification

Qualification is what a friend must do before anyone is paid.

qualificationThe friend qualifies when…
{ kind: "signup" }They sign in and accept the invitation.
questCompleted(welcome)They complete the quest you name.
{ kind: "external" }Your server tells Domino they qualify, after checking the condition itself.

A member has one inviter, but each program decides on its own whether that friend qualifies. Each friend counts once per program. Completing a quest again, or your server qualifying them again, doesn't add another friend or pay fixed rewards twice.

  • Several actions. Qualify through a quest that counts actions, such as three verified posts, and name that quest. See qualification with counters.
  • A condition that can be withdrawn. Use { kind: "external" }. Your server tells Domino the friend qualifies or no longer does, and Domino adjusts the friend's rewards, milestones and bonuses each time.
  • A one-time achievement your app checks. Your app adds the member to a group once it has checked the condition. An automatic quest that requires that group completes, and the referral qualifies on that quest. Removing the member from the group later leaves the friend qualified. As with any quest qualification, the friend accepts the invitation before completing any other quest (see acceptance rules).

Fixed rewards and milestones

The inviter, the friend and each milestone can get points or tiered rewards. Badges, group memberships and Discord roles come from quests only. Tiered rewards use the same staff handover as quest rewards. Each referral reward is its own entitlement, so different qualified friends can earn separate items. Restoring a qualification reuses its original entitlement.

A referral program published by an older Domino release may still contain a Discord role. Domino keeps that program readable so an operator can archive it. When a friend qualifies, it skips the role and records referral-reward-skipped with the reason in Audit history; the qualification and the program's other rewards still go through. Publish a replacement program with points or tiered rewards, or move the role to the qualifying quest.

Percentage bonuses

A bonus gives the inviter a percentage of what their friend earns, on top of the friend's own points. The friend never loses anything.

FieldMeaning
ledgerThe point ledger the bonus reads and pays. One per bonus.
basisPointsThe rate. 1000 basis points means 10%.
questsThe quests that count. Leave it out to count every quest award to that ledger.
externaltrue also counts awards from your own server that opt in.
durationDaysOptional. Starts when the friend first qualifies. Suspensions don't extend it.
maxPointsOptional. Caps the bonus for that friend and rule.
  • Engagement rewards count. A quest's earnings include its completion award and the daily payouts of an engagement rule that reads it. A bonus in the same ledger that covers the qualifying quest, or names no quests, earns on each engagement payout. The usual limits apply: the friend must be qualified and not suspended, the program's inviter group, durationDays and maxPoints still apply, only whole points pay out, and an outstanding correction takes the payout first.
  • Leaderboard prizes don't count. Prize points aren't quest earnings and earn no bonus, even from a bonus that names no quests. The points that won the prize already earned theirs.
  • Fractions add up. Partial points build up per friend, rule and balance. Ten awards of 3 points at 10% eventually pay 3 whole points.

Inviter groups

A program can pay only inviters in one group, such as ambassadors. Friends of other members can still accept invitations and qualify, but neither side earns anything from that program: no inviter or friend reward, no milestone and no bonus.

  • Groups are checked at the time. Domino checks the inviter's group when a friend qualifies and when the friend's points are accepted. It doesn't pay missed rewards or bonuses after the inviter joins.
  • Publishing creates the group. If the environment lacks the group, publishing creates it empty. Manage its members in Console Groups.

Share and accept invitations

Your app shows each member their link and accepts the invitation for the friend after they sign in. The friend is always the signed-in member, never someone named in the request. You can also offer a box to type a code by hand. Your app decides how it all looks; Domino doesn't need a separate confirmation screen.

import { createBrowserRelayClient } from "@domino-sdk/relay/browser";

const domino = createBrowserRelayClient({ baseUrl: "/relay" });
const summary = await domino.referrals.summary();
const invitation = new URL("https://app.example.com/join");
invitation.searchParams.set("ref", summary.code);

console.log(invitation.toString());
console.log(summary.attributed, summary.programs, summary.accounts);

// On the invited friend's landing page, retain the code through sign-in.
const code = new URL(window.location.href).searchParams.get("ref");
if (code) sessionStorage.setItem("pending-referral", code);

// After sign-in, before any quest other than the program's qualifying quest completes:
const pending = sessionStorage.getItem("pending-referral");
if (pending) {
  await domino.referrals.accept({ code: pending, actionId: "accept-invitation" });
  sessionStorage.removeItem("pending-referral");
}

const history = await domino.referrals.history();
if (history.nextCursor) {
  const older = await domino.referrals.history({ cursor: history.nextCursor });
}

Acceptance rules

  • Accept early. Accept the invitation during onboarding. Completing any quest closes the window, except a referral program's qualifying quest: a friend who checked in, or claimed the entry quest at sign-in, can still accept, and accepting qualifies them in that program and pays both sides. Points the friend earned before accepting don't earn the inviter a percentage bonus. A revoked qualifying completion doesn't qualify. A leaderboard prize is an operator accounting record and doesn't close this window.
  • First inviter wins. Opening another invitation later can't replace them.
  • Invalid codes are rejected. This includes inviting yourself, invitation loops, and codes from another project or environment.
  • Retries are safe. Repeating accept with the same action ID and payload after a timeout returns the original result.

Keep the code until you know the result. Show a clear error for an invalid code or a closed invitation window.

What the reads return

CallContains
summary()The inviter's code, how many friends accepted, qualified counts and milestone progress per program, and point accounts. Accounts separate earned amounts, settled amounts and outstanding corrections. Earned amounts leave out referral payouts an operator reversed. A settled amount can include points that paid off a correction instead of reaching the balance.
history()Paginated payout history, { items, nextCursor }. Pass nextCursor as the next call's cursor.

Neither response shows invited friends' identities, balances or detailed activity.

Award external points and qualify a friend

Your own server can qualify friends for programs that use { kind: "external" }, and award points that count toward bonuses. It uses a server-side management key scoped to the project and environment.

OperationRequired scope
External point writespoints
Qualification and moderationpublish
Readsread

A hosted app's own key (DOMINO_MANAGEMENT_KEY, see Call the management API from your server) covers point writes and reads, but not qualification, which needs publish. To qualify from a hosted app, store a key with publish as an app secret and use it for those calls, as the example below does. An automation key with Publish works in test; a personal API key with publish can qualify and moderate in live too. Audit history records each change under the key: its owner for a personal key, the automation key itself for an automation key.

import { createManagementClient } from "@domino-sdk/relay/server";

const scope = {
  baseUrl: "https://relay.domino.run",
  organization: "my-organization",
  project: "community",
  environment: "test",
} as const;

// Qualification needs a key with publish, stored as an app secret.
const publisher = createManagementClient({
  ...scope,
  getToken: async () => process.env.REFERRALS_PUBLISH_KEY!,
});

// For a program whose qualification kind is external:
await publisher.referrals.qualify("friends-external", "member_existing", {
  actionId: "verified-referral-42",
  qualified: true,
  reason: "Verified the required external activity",
});

// The hosted app's own key covers point writes.
const management = createManagementClient({
  ...scope,
  getToken: async () => process.env.DOMINO_MANAGEMENT_KEY!,
});

await management.points.record({
  kind: "award",
  actionId: "external-activity-42",
  member: "member_existing",
  balance: "community",
  amount: 100,
  referralEligible: true,
  reason: "Verified external activity 42",
});

Adjustments, absolute-total updates, spending and referral rewards never generate bonuses. Reuse the source event's action ID and payload for retries. A source award and its inviter bonus are saved together, so a failed bonus never leaves a half-saved award.

Correct, suspend, and resume

Select your project and Test or Live in the Console header, then open Referrals. Select an invited friend in the invitations list, or search for anyone by name, email or ID in Find a participant. Their details open beside the list, with their programs, payouts and payout history. Enter a Reason, then choose:

WhereActionEffect
Referral payoutsSuspendKeeps the invitation and stops future payouts to both sides.
Referral payoutsResumeRestarts payouts. The suspended period is not paid afterwards.
Each programQualify / RevokeQualifies the friend for that program, or revokes their qualification.
A reward marked Needs reviewMark reconciledClears the review block once staff have resolved the original reward. It doesn't issue, delete or reclaim a physical item.

Corrections need publish access. Select an inviter to see their payout history.

Suspending covers one invitation. To stop a farmer everywhere, ban the member: no referral payouts reach them or come from their points, and their invitation code stops working. The ban can also reverse the referral payouts they already received, following the rules below.

How corrections work

  • Reversals use the original rate. A reversed bonus uses the rate recorded with the original award.
  • Revoking qualification undoes its rewards. Fully reversing the qualifying quest's award also revokes the qualification. Staff can revoke it themselves when other evidence turns out to be invalid. The friend stops counting as qualified, their past percentage bonuses are corrected, and affected fixed rewards and milestones are adjusted. Qualifying again doesn't replay earlier percentage earnings.
  • Balances never go negative. A correction takes back available points right away without touching points held for a redemption. Anything left over comes out of future referral payouts in the same balance; ordinary future earnings aren't taken. Bob's reversal can go through even after Alice spent her bonus, as long as Bob's own balance covers his reversal.
  • A reversed payout stays reversed. If an operator reverses all or part of a referral payout from point history, Domino writes those points off on the referral account. Later payouts never pay them again, whatever reconciles the account, and a later qualification correction doesn't take them a second time. Earnings that come after the reversal still pay: if a percentage bonus grows, the inviter receives only the growth. Revoking and restoring the qualification doesn't bring the reversed points back; to pay them again, award them by hand. Reusing the reversal's action ID returns its first result, and a new reversal can't exceed what's left of the payout.
  • Restoring reuses the original entitlement. When a qualification or milestone is restored, Domino first cancels its outstanding correction, then restores amounts already taken back. It never grants a second copy of a delivered tiered reward. Invalidated tiered entitlements can't be handed over until restored or reviewed by staff.

Editing a published program

Changes to a program apply to actions accepted from then on. Backdated point awards use the current rules and eligibility; their effective date still counts for leaderboards.

Qualify referrals through Discord membership

A program can count a friend once they join your Discord server: name the Discord membership quest as the qualification, qualification: questCompleted(discordMembership). Publish that quest in the same project first.

This uses Domino invitation codes and counts each friend once. It doesn't track Discord invite links or read Discord's invite counts.

Referral balance values are stable point ledger IDs. Find the ID in Console Rewards → Point ledgers. Two ledgers can share a display name, so use descriptions and IDs to tell them apart. See point ledgers.

On this page