Developer Docs / iOS SDK / Friend Referrals
Friend referral programs
Give an invited friend a benefit when they join, and reward the inviter after your chosen milestone. Configure both sides in the dashboard; use the SDK to build the sharing, progress, and reward UI that fits your app.
Independent rewards for both sides
For example, give each new friend one month of access when their verified invite is accepted. Give the inviter one month after five friends complete signup, either once or for every five qualifying friends. Cumulative milestones can also award one month at five friends and one year at ten. Each side can use a different benefit or be disabled.
The server owns eligibility, counting, approval, and fulfillment. A delivered Apple code still needs redemption. RevenueCat promotional access adds entitlement time immediately. Custom benefits are delivered by a signed webhook or recorded by the owner.
Your app owns the UI
Configure a program in the dashboard
- Open your app's Referrals page, choose live or sandbox, and create a draft program.
- Set a campaign key, number of friends, qualifying event, once/every-N frequency and optional maximum rewards, or a list of cumulative milestones with stable IDs, thresholds and benefits.
- Choose the friend's trigger separately.
invite_accepteddelivers on verified enrollment; another event waits for that event. - Configure each benefit: Apple offer code, RevenueCat entitlement and duration, or custom feature/payload. Choose automatic delivery or email-and-review.
- Set notification addresses, or leave them empty to notify the app owner. Activate the program when ready.
paid_purchase is reserved for RevenueCat-confirmed paid store transactions. Trial starts, promotional grants, and arbitrary client purchase events do not count. Other event names must exactly match the verified SDK event.
Campaign keys remain fixed after creation. Edits create program versions: earned benefits keep their original configuration, enrolled friends keep their original friend conditions, and inviter progress follows the current program configuration. New programs count from activation.
For Apple rewards, select an existing offer or create a free offer using the App Store Connect connection. Generate a batch or paste/upload codes to the relevant side's inventory. Every import requires the Apple offer ID; expired codes are excluded. Apple applies its own redemption eligibility.
Offering selection does not grant access
Use a verified account identity
Initialize GrowthCat, align its app user ID with your RevenueCat identity, and install an identity-token provider that calls your authenticated backend. Your server signs GrowthCat identity tokens with authoritative account data; your app must not hold the signing secret.
import GrowthCat
import RevenueCat
Purchases.configure(withAPIKey: "YOUR_REVENUECAT_PUBLIC_KEY", appUserID: accountID)
GrowthCat.initialize(apiKey: "YOUR_GROWTHCAT_SDK_KEY")
GrowthCat.setAppUserID(accountID)
GrowthCat.setIdentityTokenProvider { request in
// Implement this authenticated call in your app's networking layer.
// The server verifies the account and binds eventName/eventID when present.
try await accountAPI.growthCatIdentityToken(
appUserID: request.appUserID,
eventName: request.eventName,
eventID: request.eventID
)
}New-account programs require trusted account_created_at evidence. Missing evidence goes to owner review; a client-supplied new-user boolean cannot qualify an account. One friend has one inviter per program and counts once. Self-referrals are rejected.
Update RevenueCat and GrowthCat together after login or logout. Setting GrowthCat.setAppUserID("") clears the explicit override and returns to RevenueCat's current identity. Update RevenueCat first on logout. Cached referral state clears on an identity change.
Use the GrowthCat iOS integration skill
The SDK repository includes a Codex skill that inspects the app's pinned Swift package revision, reads its matching documentation and public declarations, and integrates referrals, ads, sponsorships, feedback, or privacy configuration using the app's existing architecture.
Install the skill from
https://github.com/lapreamarcelo/GrowthCatSDK-ios/tree/main/skills/growthcat-integrationAfter installation, ask Codex for the feature and screen you want:
Use $growthcat-integration to add native feedback to my Settings screen.Apps without login: native guest referrals
In the app's Referrals page, configure Guest referral security with your Apple Team ID, bundle ID and DeviceCheck key ID and .p8 key. Reserve one DeviceCheck bit exclusively for GrowthCat referral participation and acknowledge that its meaning is shared across every app under your Apple developer team. Enable App Attest for the app in Apple Developer. If your Apple App ID prefix differs from your Team ID, configure that prefix too; leaving it blank uses the Team ID. The private key stays on the backend; never bundle it in the app.
The native iOS guest flow issues a server identity, persists credentials in Keychain and requires App Attest and DeviceCheck for guest referral mutations. DeviceCheck checks previous device participation after reinstall. This enforces a device limit, not a guarantee that each device belongs to a different person. Keychain continuity alone is not proof of referral eligibility.
GrowthCat.initialize(apiKey: "YOUR_GROWTHCAT_SDK_KEY")
// Opt in only for your native app without authenticated accounts.
let guestUserID = try await GrowthCat.enableGuestIdentity()
// Align RevenueCat with guestUserID before referral or purchase flows.Call GrowthCat.enableGuestIdentity() only after configuring the dashboard and before guest referral operations. Handle verification failure in your referral UI without blocking unrelated app features. AviKit's guestIdentityEnabled integration option defaults to false and must be enabled explicitly for apps using this flow.
Unsupported devices or Apple verification outages block guest referral mutations; keep unrelated app features available. Guest creation never automatically emits signup_completed. Use invite_accepted, a verified custom event or RevenueCat-confirmed paid_purchase for qualification. Keep the separate friend referral availability switch enabled and activate the desired program.
This guest flow is native iOS only. Web integrations must use an authenticated account and a server-signed identity. Browser storage, installation IDs and client timestamps cannot establish reinstall-resistant eligibility.
Choose the codes your form accepts
Use one input and select .influencers, .friends or .both. The server identifies the type; the SDK rejects an excluded type before enrolling a friend or applying an influencer code. Existing unified calls default to .both. Program eligibility still comes from your backend rules.
let result = try await GrowthCat.applyReferralCode(code, mode: .both)
// A creator-only form:
AviReferralCodeView(mode: .influencers)
// A friend-only form:
AviReferralCodeView(mode: .friends)
// AviKit onboarding and Settings default:
options.referralCodeMode = .bothAviKit updates the title, placeholder and guidance to match. Use the same mode for AviReferralOnboarding.item and your GrowthCatSDKOptions; a Settings-only override can use SettingsConfiguration.referralCodeMode. The legacy GrowthCatReferralView continues to accept influencer codes.
Keep invitation UI available when rules allow it
Enable friend referrals per app from the Referrals page or SDK settings; the remote switch defaults to off. Disabling new invitations and enrollment preserves accepted referrals, progress and earned rewards. Existing users can invite friends regardless of the invitee age rule. Configure the new-user signup grace period (7 days by default), allow existing invitees when appropriate, and limit welcome benefits to once per account across programs in the app and workspace. The server enforces these rules using trusted account creation claims. Turning off the grace period restores the invitation/program creation cutoff rather than accepting accounts of any age.
Read program.acceptingNewReferrals before offering a new invitation or promising a new friend benefit. Availability can change when an owner pauses a program or reaches the monthly new-enrollment allowance. Recheck before enrollment; server admission is authoritative.
When unavailable, show a simple explanation to app users and keep existing progress, qualification, and earned rewards accessible. Do not hide the referral center or show developer billing details in the app. Upgrading or the next UTC month can restore new-enrollment capacity.
All plans support influencer-only, friend-only, and combined entry. Code types are independent of commercial quotas. See current plan capacities and upgrade behavior.
Show progress in any app UI
let state = try await GrowthCat.refreshReferrals()
for progress in state.progress {
print("\(progress.qualifiedCount)/\(progress.requiredCount) qualified")
print("\(progress.enrolledCount) friends enrolled")
print("\(progress.remainingCount) friends until the next reward")
print("\(progress.earnedRewards) rewards earned")
// nextThreshold is nil when a once-only program or cap is complete.
}AviKit apps can opt into AviReferralOnboarding, AviReferralCodeView, AviReferralSettingsBanner and AviReferralCenter for reusable input, sharing, progress and redemption UI. Configure an authenticated identity provider and the matching onboarding draft key; the referral applies after login before the normal paywall. Other apps can use GrowthCatReferralObserver for SwiftUI, or consume referralStateUpdates() in your own view model. A nil update clears the previous account's benefits.
import SwiftUI
struct FriendProgressView: View {
@StateObject private var referrals = GrowthCatReferralObserver()
var body: some View {
VStack(alignment: .leading) {
if let state = referrals.state {
ForEach(state.progress, id: \.programID) { progress in
Text("\(progress.qualifiedCount) friends qualified")
Text("\(progress.remainingCount) until your next reward")
}
}
if referrals.isRefreshing { ProgressView() }
if let error = referrals.errorMessage { Text(error) }
}
.task { referrals.start() }
.onDisappear { referrals.stop() }
.refreshable { await referrals.refresh() }
}
}State refreshes on foregrounding and successful qualifying-event delivery. Ready or fulfilling rewards receive bounded follow-up refreshes after 1, 3, and 10 seconds. For owner approvals, refresh explicitly or when the app returns to the foreground. refreshReferrals(programID:) returns a filtered subset; an unfiltered refresh updates global observer state.
Deliver usable benefits and redeem Apple codes
Each reward identifies its program version, inviter/friend side, milestone, status, review reason, expiry, optional offering, and benefit details. Pending-review, inventory-waiting, failed, and rejected rewards are not usable. Check reward.isUsable before displaying a redemption action.
for reward in state.rewards {
if reward.isUsable, reward.rewardType == "asc_offer_code" {
// Copies the authorized code and presents Apple's redemption sheet.
try await GrowthCat.redeemReferralReward(reward)
// Alternatively open reward.appleRedemptionURL with normal URL handling.
}
}
// After URL redemption, or when the app returns:
try await GrowthCat.refreshReferralBenefits()The redemption helper rechecks the current account's reward and refreshes RevenueCat customer information afterward. appleRedemptionURL is available only for a usable live reward. Code assignment and showing Apple's sheet do not prove redemption; redemptionStatus remains separate.
For RevenueCat benefits, read actual CustomerInfo entitlement state to unlock subscription access. The SDK refreshes customer information after promotional delivery. Custom benefits expose customPayload; signed webhook receivers must deduplicate by reward ID.
AviReferralRewardView(reward:onCustomBenefit:) shows usable delivered benefits. A configured offering opens the actual RevenueCat paywall after the current account and authorized reward are rechecked. Price and trial eligibility are confirmed by the subscription provider. Optional onCustomBenefit callbacks on the reward view, code form, settings banner and referral center let your app handle custom benefits after a fresh reward check.
AviReferralSettingsBanner(mode: .both, onCustomBenefit: { reward in
// Navigate to your app's benefit screen; authorize the feature on your server.
showBenefit(reward)
})
// Or forward the same action through AviKit settings configuration:
settingsConfiguration.referralCustomBenefitAction = { reward in
showBenefit(reward)
}Owner review, inventory and delivery recovery
The dashboard shows participants, enrolled and qualified counts, benefits earned, per-recipient delivered and pending approval/delivery counts, delivery history, qualification evidence, and audit entries. Search by app user ID, filter rewards by delivery status, and page through results. Manual-review emails link to the exact program and reward.
Owners can approve the configured benefit, reject it with a reason, select another offer or benefit, retry a failed delivery, or record a benefit already delivered externally. Eligibility can also be verified from the participant row when missing account-newness evidence requires review, including programs with friend rewards disabled.
Replenishing Apple inventory resumes approved waiting rewards. If a saved Apple batch download fails, recover that batch instead of generating another. If Apple's creation outcome is unknown, reconcile in App Store Connect and import the existing codes manually.
Refunds require review
Testing and legacy compatibility
- Exercise installed-app links, deferred attribution, explicit token entry, and enrollment with verified identities.
- Test duplicate events, competing inviters, account changes, partial progress, repeat milestones, and caps.
- Test manual approvals, eligibility review, empty and expired inventory, failed delivery, refunds, and paused programs.
- Use sandbox to simulate the same delivery states. Sandbox makes no Apple, RevenueCat, email, or webhook calls; simulated rewards never unlock production access.
- Test real Apple redemption on an iOS device with configured offers and live codes. Test real RevenueCat promotional grants in a dedicated project.
Existing influencer attribution and legacy reward rules remain supported. The dashboard retains them under Legacy reward rules. GrowthCat.rewards(), GrowthCat.rewardProgress(), and GrowthCat.track(_:properties:) are public; use the richer referral state for new friend-program integrations.
Use the SDK version that exposes these program APIs. The SDK requires RevenueCat 5.92.0 or later. Check your pinned package's documentation and public declarations before integrating.