Skip to main content

Referrals

Referral links allow users to invite friends directly into your app, unlocking viral growth. For more information and best practices, see the Virality guide.

You can attach an entryPayload to carry attribution or customization into the invited player’s first session, and then track which invites converted (joined your app).

Conversions are grouped by reference, a stable campaign key you define (for example unlock_party_mode_v1 or tiktok_campaign_feb).

note

Attribution happens the first time a user enters your app. A user who has previously entered your app is not counted as a conversion if they later open a referral link.

Open the platform share dialog with your provided title/text and a link to your app.

note

Sharing only opens the share dialog. It does not guarantee the user completes the share.

The entryPayload is embedded into the shared link. When an invited user enters your app through that link, you can read the payload as described in Entry payload.

JestSDK.referrals.shareReferralLink(options)

JestSDK.referrals.shareReferralLink({
// A stable campaign key you choose to label invites (for example a feature or campaign name).
reference: string;

// Optional metadata that is passed to the invited user when they enter the app
entryPayload?: Record<string, unknown>;

// Optional title/text for the share dialog (platform dependent)
shareTitle?: string;
shareText?: string;

// Optional onboarding game slug to route invited players through first
onboardingSlug?: string;

// Optional notification templates sent to the referrer when invited players convert.
// Each template activates at or above its minConversionCount; the platform picks the
// template with the highest matching threshold and a variant from within it.
notificationTemplates?: {
minConversionCount: number;
variants: {
body: string;
ctaText: string;
title?: string | null;
imageReference?: string | null;
}[];
}[];

// Optional base64 data URL of an image to use as the og:image when the
// referral link is opened. The platform decodes, hashes, dedups, hosts
// the bytes on its CDN, and stores the resulting URL on the referral.
// Accepted MIME: image/png, image/jpeg, image/webp, image/gif. Data URL ≤ 2 MB.
shareImage?: string;
}): Promise<{
// True if the user dismissed the share dialog.
canceled: boolean;
}>;

Here’s an example:

const { canceled } = await JestSDK.referrals.shareReferralLink({
reference: "unlock_party_mode_v1", // Unlock Party Mode after enough successful invites
entryPayload: {
feature: "party_mode",
// Optional: include referrer-specific context for the invited player to read on entry
// (for example, a chosen pet name, loadout, or a short invite code).
referrer: { name: "Ava", petName: "Pickles" },
},
shareTitle: "Come play this with me",
shareText: "Join my game. I want to unlock Party Mode with you.",
});

if (canceled) {
// The user dismissed the share dialog.
}

shareReferralLink can throw an INVALID_ARGUMENTS error if the provided options do not match the required schema.

Pass a base64 data URL via shareImage to make that image the OG preview on the referral's landing page — so when someone forwards the link or pastes it into a messaging app, they see your image instead of the app's default share card. The platform hosts the bytes on its CDN; you don't need a hosting setup of your own. Identical bytes are deduplicated by content hash, so re-sharing the same image is free.

Typical use cases: a score card the game rendered after a play, a personalized invite generated from the referrer's name, or a current-leaderboard snapshot.

Constraints: image/png, image/jpeg, image/webp, or image/gif; the data URL must be at most 2 MB. Oversize, wrong-MIME, or non-decodable inputs are rejected. If shareImage is omitted, the referral link uses the app's static share image. Animated GIFs are hosted unmodified, so the preview stays animated in messaging apps that support it.

Render the image to a canvas and pass canvas.toDataURL(...) as shareImage. Oversize, wrong-MIME, or non-decodable inputs reject with BAD_REQUEST.

const canvas = document.querySelector("canvas")!;
await JestSDK.referrals.shareReferralLink({
reference: "share_score_v1",
shareTitle: "I scored 9,001!",
shareText: "Can you beat my score?",
shareImage: canvas.toDataURL("image/png"),
});

List referral conversions

Retrieve referral conversions for the current user, grouped by reference.

Conversions include only invited users who complete registration.

JestSDK.referrals.listReferrals()

JestSDK.referrals.listReferrals(): Promise<{
referrals: {
[reference: string]: { playerId: string; joinedAt: string }[];
};
referralsSigned: string;
}>;
const { referrals, referralsSigned } = await JestSDK.referrals.listReferrals();

const partyModeConversions = referrals["unlock_party_mode_v1"] ?? [];
console.log("Party Mode conversions:", partyModeConversions.length);

// Recommended: send `referralsSigned` to your backend and verify it before granting rewards.

If you grant rewards for referrals, verify referralsSigned on your backend instead of trusting client-reported conversions. This is the same pattern used for signed player payloads in the Player module and signed purchase payloads in the Payments module.

referralsSigned is an HS256 JWS signed with your app’s shared secret. The payload has this shape:

type ReferralsSignedPayload = {
referrals: Record<string, Array<{ playerId: string; joinedAt: string }>>;
aud: string; // game id
sub: string; // referrer player id
};
// npm i jose
import { jwtVerify } from "jose";

export async function verifyReferralsSigned(token: string, gameId: string) {
const secretBase64 = process.env.JWS_SECRET!;
const secret = Buffer.from(secretBase64, "base64");

const { payload } = await jwtVerify(token, secret, {
algorithms: ["HS256"],
audience: gameId,
});

return payload as {
referrals: Record<string, Array<{ playerId: string; joinedAt: string }>>;
aud: string;
sub: string;
};
}

Example: Unlock a feature after 3 invites

Suppose you want to unlock a feature (for example, “Party Mode”) after a player gets 3 successful invites.

Use a dedicated reference for this feature gate, for example:

  • reference: "unlock_party_mode_v1"

There are two common ways to implement this:

  • Client-only (not server verified): simplest, good for low-stakes UX (cosmetics, UI hints). This can be spoofed by a modified client.
  • Server verified: recommended for anything that affects entitlements, currency, or competitive balance. The server verifies referralsSigned before unlocking.

Client-only (not server verified)

const { referrals } = await JestSDK.referrals.listReferrals();
const inviteCount = (referrals["unlock_party_mode_v1"] ?? []).length;

if (inviteCount >= 3) {
unlockPartyModeLocally();
} else {
showInviteProgress({ inviteCount, required: 3 });
}

Client: send referralsSigned to your backend (and authenticate the user using your normal mechanism).

const { referralsSigned } = await JestSDK.referrals.listReferrals();

await fetch("/api/referrals/unlock-party-mode", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
referralsSigned,
}),
});

Server: verify the JWS, count conversions for the reference, then unlock the feature in persistent state.

import { jwtVerify } from "jose";

export async function unlockPartyModeHandler(req: {
body: { referralsSigned: string };
}) {
const secretBase64 = process.env.JWS_SECRET!;
const secret = Buffer.from(secretBase64, "base64");
const gameId = process.env.GAME_ID!;

const { payload } = await jwtVerify(req.body.referralsSigned, secret, {
algorithms: ["HS256"],
audience: gameId,
});

const referrals = payload.referrals as Record<
string,
Array<{ playerId: string; joinedAt: string }>
>;

const reference = "unlock_party_mode_v1"; // hardcode server-side; do not trust client input
const inviteCount = (referrals?.[reference] ?? []).length;

if (inviteCount < 3) {
return { unlocked: false, inviteCount };
}

// Persist the entitlement (idempotent write recommended).
await grantPartyModeToPlayer({ playerId: payload.sub as string });

return { unlocked: true, inviteCount };
}

Testing referrals

To run a full referral loop against an uploaded build, use two sandbox users — one as the referrer, one as the invitee. See Test referrals for the step-by-step recipe.