Skip to content
InfluToDocs

Reference

SDK reference

Every public method of the five InfluTo SDKs — exact signatures per platform, result fields, what throws, what fails soft, caching and local storage.

This page lists every public method of the InfluTo SDKs, with the exact signature on each platform. It covers React Native 1.6.1, iOS 1.1.1, Android 1.1.1, Flutter 1.1.1 and web 1.0.1. For setup steps, start with your platform's install guide; for the HTTP calls behind each method, see the HTTP API.

At a glance

Method What it does Throws? Notes
initialize Connects the SDK with your app's key Yes Call once at startup; every other method needs it
checkAttribution Decides once per install whether a creator referred it Only if called before initialize succeeded (not on iOS) Result is stored; network errors return "not attributed"
identifyUser Links your user ID to this install No Lets purchases without the RevenueCat attribute still attribute
trackEvent Records a custom funnel event No Never creates a conversion or commission
getActiveCampaigns Lists the app's active public campaigns No [] on error
getReferralCode Reads the stored code No Local read
getPrefilledCode The stored code, only when this install is attributed No Local read; use it to prefill a code field
validateCode Checks a typed code without applying it No NETWORK_ERROR on failure
setReferralCode Applies a code: stores it, tags RevenueCat, records it No Returns the free-access fields
applyCode validateCode, then setReferralCode if valid No Adds applied; no free-access fields
checkAccess Whether InfluTo grants this user free access No Positive result cached for 5 minutes
reportPurchase Sends a store-signed purchase for verification (store-direct) Yes 503 means retry
Automatic purchase capture Reports store purchases for you on store-direct platforms Varies Names differ per platform
clearAttribution Forgets the local attribution No Local only; for testing

Conventions

  • Fail-soft. Only initialize and reportPurchase throw on network or server errors. Everything else returns a safe value (attributed: false, [], valid: false, success: false, hasAccess: false) and logs the error when debug is on.
  • Before initialize succeeds, methods return their fail-soft values without a request. checkAttribution and reportPurchase throw instead (on iOS, checkAttribution returns attributed: false with the message SDK not initialized). On React Native, enableAutoPurchaseCapture and syncExistingPurchases also throw.
  • Codes are trimmed and uppercased by the SDK before they're sent. The server also ignores any character outside A–Z 0–9 _ - when it looks a code up, but the SDK stores — and writes to RevenueCat — the code as you passed it, so strip other characters (spaces, invisible characters from copy-paste) in your own input field. See code rules.
  • Async style. React Native and web return Promises; iOS methods are async statics on InfluTo; Flutter uses InfluTo.instance and Futures; Android methods are suspend functions on object InfluTo, and every network method also has an overload whose last parameter is a (Result<T>) -> Unit callback, for Java or non-coroutine code.
  • Field names. React Native and web keep the server's snake_case in validation and purchase results (campaign_type, error_code, event_type); iOS, Android and Flutter use camelCase (campaignType, errorCode, eventType). See Result types.

initialize

Connects the SDK with your app's key (POST /sdk/init). Call it once when the app starts, before anything else.

TSX
// initialize(config: InfluToConfig): Promise<void>
import InfluTo from '@influto/react-native-sdk';

await InfluTo.initialize({ apiKey: 'it_…', debug: __DEV__, appVersion: '1.4.0' });
Swift
// static func initialize(_ config: InfluToConfig) async throws
import InfluTo

try await InfluTo.initialize(InfluToConfig(apiKey: "it_…", debug: true, appVersion: "1.4.0"))
Kotlin
// suspend fun initialize(context: Context, config: InfluToConfig)
// fun initialize(context: Context, config: InfluToConfig, callback: (Result<Unit>) -> Unit)
import to.influ.sdk.InfluTo
import to.influ.sdk.InfluToConfig

lifecycleScope.launch {
    InfluTo.initialize(applicationContext, InfluToConfig(apiKey = "it_…", appVersion = BuildConfig.VERSION_NAME))
}
Dart
// Future<void> initialize(InfluToConfig config)
import 'package:influto/influto.dart';

await InfluTo.instance.initialize(const InfluToConfig(apiKey: 'it_…', appVersion: '1.4.0'));
TypeScript
// initialize(config: InfluToConfig): Promise<InitResult>
import InfluTo from '@influto/web';
import { capacitorAdapters } from '@influto/web/capacitor';

const init = await InfluTo.initialize({ apiKey: 'it_…', appVersion: '1.4.0', ...(await capacitorAdapters()) });

Configuration

Field Platforms Default What it does
apiKey All required Your app's SDK key (it_…) from Setup & SDK. One key covers sandbox and production.
debug All false Logs calls and errors.
appVersion All "unknown" Your app's version, sent with /sdk/init.
autoCapture All true Reports store purchases automatically on store-direct platforms. On Flutter it's read by the influto_iap observer. See Automatic purchase capture.
oneTimeProductIds React Native, Android, web none Your Android one-time and consumable product IDs, so captured purchases are validated as one-time rather than subscriptions. Array on React Native and web, Set<String> on Android.
revenueCatAttributeSetter iOS, Android nil / null Function the SDK calls with the RevenueCat attributes. Android falls back to finding the RevenueCat SDK by reflection when it's not set.
revenueCatHook Flutter null Future<void> Function(Map<String, String>), same purpose.
revenueCat Web none A function or an object with setAttributes; helpers in @influto/web/revenuecat.
httpClient Flutter default client Injectable http.Client (tests, custom transport).
runtime, storage, device, http Web detected Runtime and storage adapters; capacitorAdapters() and cordovaAdapters() fill them in.
captureLaunchCode Web true Reads influto_code / influto_click from the page URL during initialize.
cleanUrl Web true Removes the influto_* parameters from the address bar after reading them.
purchaseSource Web none Store purchase source for automatic capture.
apiURL (iOS), apiUrl iOS, Android, Flutter https://influ.to/api Base URL override for testing.

React Native has no RevenueCat option: if react-native-purchases is installed, the SDK finds it and sets the attributes itself.

Behavior

  • Throws on a bad key, a network failure or a server error. Catch it and continue; the SDK stays uninitialized for this launch (see Conventions) and you can try again on the next one.
  • If the response says the platform is store-direct (store_direct: true) and autoCapture isn't false, it starts automatic purchase capture.
  • Web only: reads a referral code from the launch URL before the request, so a deep-linked referral survives a failed init, and returns the /sdk/init response as InitResult (app_id, app_name, attribution_window_hours, campaigns, store_direct, initialized).

checkAttribution

Decides whether this install came from a creator. The first call sends POST /sdk/track-install; the result is stored and every later call returns it without a request.

TSX
// checkAttribution(): Promise<AttributionResult>
const attribution = await InfluTo.checkAttribution();
if (attribution.attributed) console.log(attribution.referralCode);
Swift
// static func checkAttribution() async -> AttributionResult
let attribution = await InfluTo.checkAttribution()
if attribution.attributed, let code = attribution.referralCode { print(code) }
Kotlin
// suspend fun checkAttribution(): AttributionResult
val attribution = InfluTo.checkAttribution()
if (attribution.attributed) Log.d("Referral", attribution.referralCode ?: "")
Dart
// Future<AttributionResult> checkAttribution()
final attribution = await InfluTo.instance.checkAttribution();
if (attribution.attributed) debugPrint(attribution.referralCode);
TypeScript
// checkAttribution(): Promise<AttributionResult>
const attribution = await InfluTo.checkAttribution();
if (attribution.attributed) console.log(attribution.referralCode);

Returns an AttributionResult.

  • Decided once per install. Both outcomes, attributed and organic, are stored under @influto/attribution. A failed request returns { attributed: false, message: "Error checking attribution" } and stores nothing, so the next launch asks again.
  • When attributed, the code is stored under @influto/influto_code and the SDK sets the RevenueCat attributes influto_code = the code and influto_referral = "true" (a string). That happens automatically on React Native (with react-native-purchases installed) and Android; iOS, Flutter and web need the setter, hook or binding from initialize.
  • attributionMethod is link_param (a link carried the code into the app), ip_device_match (link-click matching) or manual_entry (a code applied with setReferralCode). See attribution.
  • Web: a code captured from the launch URL or the Play Install Referrer is sent along and claimed exactly (link_param), and it replaces a stored organic result. Concurrent calls share one request.
  • Throws before initialize succeeded, except on iOS (see Conventions).

identifyUser

Tells InfluTo who the user is — typically the RevenueCat app user ID, or your own user ID (POST /sdk/identify).

TSX
// identifyUser(appUserId: string, properties?: Record<string, any>): Promise<void>
await InfluTo.identifyUser(userId, { plan: 'free' });
Swift
// static func identifyUser(_ appUserId: String, properties: [String: AnyCodable]? = nil) async
await InfluTo.identifyUser(userID, properties: ["plan": AnyCodable("free")])
Kotlin
// suspend fun identifyUser(appUserId: String, properties: Map<String, Any?>? = null)
InfluTo.identifyUser(userId, mapOf("plan" to "free"))
Dart
// Future<void> identifyUser(String appUserId, {Map<String, dynamic>? properties})
await InfluTo.instance.identifyUser(userId, properties: {'plan': 'free'});
TypeScript
// identifyUser(appUserId: string, properties?: Record<string, any>): Promise<void>
await InfluTo.identifyUser(userId, { plan: 'free' });
  • Stores the ID under @influto/app_user_id (before the request, so it's kept even if the request fails). checkAccess and reportPurchase use it when you don't pass a user ID.
  • When the server can match this install's attributed click, it attaches the ID to it (the first identify wins). If a RevenueCat event later arrives without the influto_code attribute, InfluTo looks for an attributed click with the purchaser's app user ID — so call it early in the first session, as soon as you know the user.
  • Call it once per launch, not on every render: identical calls (same key and user) within 60 seconds are answered from memory, and the endpoint is rate limited to 2 requests per second per IP (limits).
  • Fail-soft; does nothing before initialize.

trackEvent

Records a custom event for the funnel on your dashboard (POST /sdk/event).

TSX
// trackEvent(options: TrackEventOptions): Promise<void>
// TrackEventOptions { eventType: string; appUserId: string; properties?: Record<string, any>; referralCode?: string; eventId?: string }
await InfluTo.trackEvent({ eventType: 'paywall_viewed', appUserId: userId, properties: { placement: 'onboarding' } });
Swift
// static func trackEvent(_ options: TrackEventOptions) async
// TrackEventOptions(eventType:appUserId:properties:referralCode:eventId:)
await InfluTo.trackEvent(TrackEventOptions(eventType: "paywall_viewed", appUserId: userID,
                                           properties: ["placement": AnyCodable("onboarding")]))
Kotlin
// suspend fun trackEvent(eventType: String, appUserId: String, properties: Map<String, Any?>? = null,
//                        referralCode: String? = null, eventId: String? = null)
InfluTo.trackEvent(eventType = "paywall_viewed", appUserId = userId, properties = mapOf("placement" to "onboarding"))
Dart
// Future<void> trackEvent(TrackEventOptions options)
// TrackEventOptions({required eventType, required appUserId, properties, referralCode, eventId})
await InfluTo.instance.trackEvent(const TrackEventOptions(
    eventType: 'paywall_viewed', appUserId: 'user_123', properties: {'placement': 'onboarding'}));
TypeScript
// trackEvent(options: TrackEventOptions): Promise<void>
await InfluTo.trackEvent({ eventType: 'paywall_viewed', appUserId: userId, properties: { placement: 'onboarding' } });
  • Never creates a conversion or a commission. Money comes only from verified purchases (RevenueCat or store-direct).
  • Duplicates are dropped. Without an eventId, the SDK generates one: for trial_started, subscription_purchased and subscription_renewed it's derived from the event type, user and properties, so these count once per user and property set even across launches; other events get a random UUID. Pass your own eventId to control this. The server also drops an identical event without an ID sent within 60 seconds.
  • Fail-soft; does nothing before initialize.

getActiveCampaigns

Lists the app's active public campaigns (GET /sdk/campaigns), for showing offers in the app.

TSX
// getActiveCampaigns(): Promise<Campaign[]>
const campaigns = await InfluTo.getActiveCampaigns();
Swift
// static func getActiveCampaigns() async -> [Campaign]
let campaigns = await InfluTo.getActiveCampaigns()
Kotlin
// suspend fun getActiveCampaigns(): List<Campaign>
val campaigns = InfluTo.getActiveCampaigns()
Dart
// Future<List<Campaign>> getActiveCampaigns()
final campaigns = await InfluTo.instance.getActiveCampaigns();
TypeScript
// getActiveCampaigns(): Promise<Campaign[]>
const campaigns = await InfluTo.getActiveCampaigns();

Each Campaign has id, name, description and the creator commission percentage. Private and invite-only campaigns aren't included. Returns an empty list before initialize or on any error.

getReferralCode

Returns the stored code — from attribution or from a code the user applied — or null. No request.

TSX
// getReferralCode(): Promise<string | null>
const code = await InfluTo.getReferralCode();
Swift
// static func getReferralCode() async -> String?
let code = await InfluTo.getReferralCode()
Kotlin
// fun getReferralCode(): String?   (not suspend)
val code = InfluTo.getReferralCode()
Dart
// Future<String?> getReferralCode()
final code = await InfluTo.instance.getReferralCode();
TypeScript
// getReferralCode(): Promise<string | null>
const code = await InfluTo.getReferralCode();

Reads @influto/influto_code. This is the code reportPurchase sends by default.

getPrefilledCode

Returns the stored code only when this install is attributed — use it to prefill a code field. No request.

TSX
// getPrefilledCode(): Promise<string | null>
const prefill = await InfluTo.getPrefilledCode();
Swift
// static func getPrefilledCode() async -> String?
let prefill = await InfluTo.getPrefilledCode()
Kotlin
// fun getPrefilledCode(): String?   (not suspend)
val prefill = InfluTo.getPrefilledCode()
Dart
// Future<String?> getPrefilledCode()
final prefill = await InfluTo.instance.getPrefilledCode();
TypeScript
// getPrefilledCode(): Promise<string | null>
const prefill = await InfluTo.getPrefilledCode();

Reads the stored attribution: the code when attributed is true (from a link or an applied code), otherwise null.

validateCode

Checks a code the user typed, without applying it (POST /sdk/validate-code). Matches creator codes and free-access codes.

TSX
// validateCode(code: string): Promise<CodeValidationResult>
const check = await InfluTo.validateCode(input);
if (!check.valid) showError(check.error_code);
Swift
// static func validateCode(_ code: String) async -> CodeValidationResult
let check = await InfluTo.validateCode(input)
if !check.valid { print(check.errorCode ?? "") }
Kotlin
// suspend fun validateCode(code: String): CodeValidationResult
val check = InfluTo.validateCode(input)
if (!check.valid) Log.d("Code", check.errorCode?.name ?: "")
Dart
// Future<CodeValidationResult> validateCode(String code)
final check = await InfluTo.instance.validateCode(input);
if (!check.valid) debugPrint(check.errorCode);
TypeScript
// validateCode(code: string): Promise<CodeValidationResult>
const check = await InfluTo.validateCode(input);
if (!check.valid) showError(check.error_code);

Returns a CodeValidationResult.

  • Nothing is stored and RevenueCat isn't touched.
  • A free-access code comes back valid with campaign_type FREE_ACCESS and the campaign name Free Access.
  • For creator codes, influencer.name and the message text ("Valid code! Referred by …") contain the creator's personal name. Don't show them unless your creators agreed.
  • Fail-soft: before initialize or on a network error, returns { valid: false, error_code: "NETWORK_ERROR" }.

setReferralCode

Applies a code: stores it locally as a manual_entry attribution, sets the RevenueCat attributes, and records it with InfluTo (POST /sdk/set-referral-code). For free-access codes, this is the call that grants access.

TSX
// setReferralCode(code: string, appUserId?: string): Promise<SetCodeResult>
const result = await InfluTo.setReferralCode(code, userId);
if (result.grantsAccess) unlockPremium();
Swift
// static func setReferralCode(_ code: String, appUserId: String? = nil) async -> SetCodeResult
let result = await InfluTo.setReferralCode(code, appUserId: userID)
if result.grantsAccess == true { unlockPremium() }
Kotlin
// suspend fun setReferralCode(code: String, appUserId: String? = null): SetCodeResult
val result = InfluTo.setReferralCode(code, userId)
if (result.grantsAccess == true) unlockPremium()
Dart
// Future<SetCodeResult> setReferralCode(String code, {String? appUserId})
final result = await InfluTo.instance.setReferralCode(code, appUserId: userId);
if (result.grantsAccess == true) unlockPremium();
TypeScript
// setReferralCode(code: string, appUserId?: string): Promise<SetCodeResult>
const result = await InfluTo.setReferralCode(code, userId);
if (result.grantsAccess) unlockPremium();

Returns a SetCodeResult.

  • Only an accepted code is kept. When the server answers success: true, the SDK stores the code (as the server matched it, look-alike characters corrected), records the referral and writes the RevenueCat attributes. A rejected code leaves nothing behind. Older versions stored the code before the answer (before React Native 1.6.1, iOS/Android/Flutter 1.1.1, web 1.0.1). For codes users type, applyCode validates first and returns readable error codes.
  • Free-access codes need a user ID. appUserId defaults to the user from identifyUser (React Native 1.6.1 and iOS/Android/Flutter 1.1.1 or later; the web SDK always did). With no user at all the server answers success: false ("Could not identify your account"). iOS, Android and Flutter also store a passed appUserId as the identified user.
  • If this creator's link already attributed an install from the same IP address within the attribution window, the server answers success: true with the message "Code already attributed from link click" and records nothing new.
  • An unknown or inactive code is a 404 from the server, which the SDK returns as success: false.
  • Gate premium on checkAccess (or the store entitlement); grantsAccess is for unlocking immediately.

applyCode

The usual call for a typed code: runs validateCode, and if the code is valid, setReferralCode.

TSX
// applyCode(code: string, appUserId?: string): Promise<CodeValidationResult & { applied?: boolean }>
const result = await InfluTo.applyCode(code, userId);
if (result.applied && result.campaign?.campaign_type === 'FREE_ACCESS') {
  const { hasAccess } = await InfluTo.checkAccess(userId);
  if (hasAccess) unlockPremium();
}
Swift
// static func applyCode(_ code: String, appUserId: String? = nil) async -> CodeValidationResult
let result = await InfluTo.applyCode(code, appUserId: userID)
if result.applied == true, result.campaign?.campaignType == "FREE_ACCESS" {
    let access = await InfluTo.checkAccess(appUserId: userID)
    if access.hasAccess { unlockPremium() }
}
Kotlin
// suspend fun applyCode(code: String, appUserId: String? = null): CodeValidationResult
val result = InfluTo.applyCode(code, userId)
if (result.applied == true && result.campaign?.campaignType == "FREE_ACCESS") {
    if (InfluTo.checkAccess(userId).hasAccess) unlockPremium()
}
Dart
// Future<CodeValidationResult> applyCode(String code, {String? appUserId})
final result = await InfluTo.instance.applyCode(code, appUserId: userId);
if (result.applied == true && result.campaign?.campaignType == 'FREE_ACCESS') {
  final access = await InfluTo.instance.checkAccess(appUserId: userId);
  if (access.hasAccess) unlockPremium();
}
TypeScript
// applyCode(code: string, appUserId?: string): Promise<CodeValidationResult & { applied?: boolean }>
const result = await InfluTo.applyCode(code, userId);
if (result.applied && result.campaign?.campaign_type === 'FREE_ACCESS') {
  const { hasAccess } = await InfluTo.checkAccess(userId);
  if (hasAccess) unlockPremium();
}
  • Returns the validation result plus applied: true when setReferralCode succeeded, false when the code was invalid or applying it failed.
  • It does not return the free-access fields (freeAccess, grantsAccess, entitlement, expiresAt). To react to a free-access code, check campaign_type / campaignType for FREE_ACCESS and then call checkAccess, as above — or call validateCode and setReferralCode yourself and read grantsAccess.

checkAccess

Asks InfluTo whether this user currently has free access from a free-access code (GET /sdk/access). Works the same for RevenueCat and store-direct apps.

TSX
// checkAccess(appUserId?: string): Promise<AccessResult>
const premium = entitlementActive || (await InfluTo.checkAccess(userId)).hasAccess;
Swift
// static func checkAccess(appUserId: String? = nil) async -> AccessResult
let access = await InfluTo.checkAccess(appUserId: userID)
let premium = entitlementActive || access.hasAccess
Kotlin
// suspend fun checkAccess(appUserId: String? = null): AccessResult
val premium = entitlementActive || InfluTo.checkAccess(userId).hasAccess
Dart
// Future<AccessResult> checkAccess({String? appUserId})
final premium = entitlementActive || (await InfluTo.instance.checkAccess(appUserId: userId)).hasAccess;
TypeScript
// checkAccess(appUserId?: string): Promise<AccessResult>
const premium = entitlementActive || (await InfluTo.checkAccess(userId)).hasAccess;

Returns an AccessResult.

  • Uses appUserId, or the ID stored by identifyUser / setReferralCode. With no user ID it returns hasAccess: false without a request.
  • Caching: a positive result is cached for 5 minutes under @influto/access (iOS, Android and Flutter also keep it in memory). A negative result is never cached, so a code redeemed a moment ago shows up on the next call.
  • The server decides: seat limits and expiry are enforced there, and blocking or expiring a code removes access for everyone who redeemed it once their cached result expires.
  • Recommended premium gate: your store or RevenueCat entitlement or checkAccess().hasAccess.
  • Fail-soft: any error returns hasAccess: false. clearAttribution doesn't clear it.

reportPurchase

Store-direct only. Sends the store's signed proof of a purchase so InfluTo can verify it with Apple or Google and record the commission (POST /sdk/purchase). If automatic capture is on, you don't call this — use one reporting path, not both.

TSX
// reportPurchase(options: ReportPurchaseOptions): Promise<PurchaseResult>
// { platform?, signedTransaction?, purchaseToken?, productId?, price?, currency?, appUserId?, referralCode? }
await InfluTo.reportPurchase({ platform: 'ios', signedTransaction: jws });          // StoreKit 2 JWS
await InfluTo.reportPurchase({ platform: 'android', purchaseToken });              // Play Billing token
Swift
// static func reportPurchase(verification: VerificationResult<Transaction>, appUserId: String? = nil,
//                            referralCode: String? = nil) async throws -> PurchaseResult
// static func reportPurchase(jws: String, appUserId: String? = nil,
//                            referralCode: String? = nil) async throws -> PurchaseResult
let outcome = try await product.purchase(options: [.appAccountToken(InfluTo.appAccountToken(forUserID: userID))])
if case .success(let verification) = outcome {
    _ = try await InfluTo.reportPurchase(verification: verification, appUserId: userID)
    // finish the transaction as usual
}
Kotlin
// suspend fun reportPurchase(purchaseToken: String, appUserId: String? = null, referralCode: String? = null,
//                            productId: String? = null, price: Double? = null, currency: String? = null): PurchaseResult
InfluTo.reportPurchase(purchaseToken = purchase.purchaseToken)                       // subscription
InfluTo.reportPurchase(purchaseToken = purchase.purchaseToken,
                       productId = "coins_100", price = 4.99, currency = "USD")       // one-time product
Dart
// Future<PurchaseResult> reportPurchase({required String platform, String? signedTransaction,
//     String? purchaseToken, String? productId, double? price, String? currency,
//     String? appUserId, String? referralCode})
await InfluTo.instance.reportPurchase(platform: 'android', purchaseToken: token);
TypeScript
// reportPurchase(options: ReportPurchaseOptions): Promise<PurchaseResult>
await InfluTo.reportPurchase({ platform: 'android', purchaseToken });

Returns a PurchaseResult.

Option Meaning
platform ios or android. Defaults to the current platform on React Native and in Capacitor/Cordova; required on Flutter and in a plain browser. iOS and Android set it themselves.
signedTransaction iOS: the StoreKit 2 jwsRepresentation (the signed JWS, not the decoded JSON). The iOS SDK takes the VerificationResult or the JWS string.
purchaseToken Android: the Play Billing purchaseToken.
productId, price, currency Android one-time products only: the product ID (routes the purchase to one-time validation) and the price paid with its ISO currency, from ProductDetails. Omit them for subscriptions.
appUserId Defaults to the ID from identifyUser.
referralCode Defaults to the stored code, so the first transaction binds to the referral.
  • Throws on any error: 400 while the platform isn't switched to store-direct (so it's safe to ship first) or when the proof fails verification, 409 for an Android one-time purchase that isn't in the PURCHASED state yet, 503 when the currency rate is unavailable — retry later. Error types per platform are in Limits and errors.
  • Reporting the same purchase twice is safe; the server keys purchases on Apple's original transaction ID and Google's purchase token.
  • iOS: InfluTo.appAccountToken(forUserID:) gives a stable token to pass to product.purchase(options:) — see below.

Automatic purchase capture

On store-direct platforms the SDKs can report purchases for you. Capture starts only when /sdk/init returns store_direct: true for the platform and autoCapture isn't false, so RevenueCat apps are never affected. It also back-fills existing purchases once, and remembers what it sent so nothing is reported twice.

Platform Needs Start / stop One-shot back-sync
React Native expo-iap or react-native-iap installed Starts in initialize; enableAutoPurchaseCapture({ backSync?, oneTimeProductIds? }), disableAutoPurchaseCapture() syncExistingPurchases()
iOS Nothing (StoreKit 2, iOS 16+) Starts in initialize; startPurchaseObservation(), stopPurchaseObservation() syncExistingPurchases()
Android to.influ:android-sdk-billing Starts in initialize; InfluToBilling.startObservation(context, oneTimeProductIds), InfluToBilling.stopObservation() InfluToBilling.syncExistingPurchases(context)
Flutter influto_iap You start it: InfluToPurchaseObserver(oneTimeProductIds: …).enable() after initialize; disable() syncExisting()
Web A purchaseSource Starts in initialize; enableAutoPurchaseCapture(source?, opts?), disableAutoPurchaseCapture() syncExistingPurchases()
TSX
// enableAutoPurchaseCapture(opts?: { backSync?: boolean; oneTimeProductIds?: string[] }): Promise<PurchaseSyncResult>
// disableAutoPurchaseCapture(): void
// syncExistingPurchases(): Promise<PurchaseSyncResult>
const { fetched, sent, failed } = await InfluTo.syncExistingPurchases();
Swift
// static func startPurchaseObservation() async
// static func stopPurchaseObservation() async
// @discardableResult static func syncExistingPurchases() async -> PurchaseSyncResult
let sync = await InfluTo.syncExistingPurchases()
Kotlin
// to.influ.sdk.billing.InfluToBilling (artifact to.influ:android-sdk-billing)
// fun startObservation(context: Context, oneTimeProductIds: Set<String> = emptySet())
// fun stopObservation()
// suspend fun syncExistingPurchases(context: Context): PurchaseSyncResult
// suspend fun report(context: Context, purchase: Purchase, oneTime: Boolean = false,
//                    price: Double? = null, currency: String? = null): Boolean
val sync = InfluToBilling.syncExistingPurchases(applicationContext)
Dart
// package:influto_iap/influto_iap.dart
// InfluToPurchaseObserver({InAppPurchase? iap, Set<String> oneTimeProductIds = const {}, bool debug = false})
// Future<PurchaseSyncResult> enable({bool backSync = true}); void disable(); Future<PurchaseSyncResult> syncExisting()
final observer = InfluToPurchaseObserver(oneTimeProductIds: {'coins_100'});
final sync = await observer.enable();
TypeScript
// enableAutoPurchaseCapture(source?: PurchaseSource, opts?: { backSync?: boolean; oneTimeProductIds?: string[] }): Promise<PurchaseSyncResult>
// disableAutoPurchaseCapture(): void
// syncExistingPurchases(): Promise<PurchaseSyncResult>
import { capgoNativePurchasesSource } from '@influto/web/capacitor';

await InfluTo.initialize({ apiKey: 'it_…', purchaseSource: capgoNativePurchasesSource(NativePurchases) });
  • The back-sync returns a PurchaseSyncResult.
  • Android: InfluToBilling.report(context, purchase, …) reports a Purchase from your own BillingClient without a second client; it returns true when the purchase was newly reported. startObservation runs its own BillingClient — use it only when nothing else handles billing.
  • Flutter: the observer checks InfluTo.instance.autoCaptureEnabled, so call enable() after initialize has finished.
  • Web purchase sources: capgoNativePurchasesSource(NativePurchases, opts?) from @influto/web/capacitor, cordovaPurchaseSource(store, opts?) from @influto/web/cordova, and digitalGoodsSource(opts?) from @influto/web/pwa for Google Play in a Trusted Web Activity. The Digital Goods API has no purchase event, so call syncExistingPurchases() after each checkout.
  • Android one-time products: list them in oneTimeProductIds; a Play purchase doesn't say whether it's a subscription.

clearAttribution

Forgets the local attribution. For testing; there's no need to call it on logout.

TSX
// clearAttribution(): Promise<void>
await InfluTo.clearAttribution();
Swift
// static func clearAttribution() async
await InfluTo.clearAttribution()
Kotlin
// fun clearAttribution()   (not suspend)
InfluTo.clearAttribution()
Dart
// Future<void> clearAttribution()
await InfluTo.instance.clearAttribution();
TypeScript
// clearAttribution(): Promise<void>
await InfluTo.clearAttribution();
  • Removes @influto/attribution, @influto/influto_code and @influto/app_user_id. The web SDK removes the attribution, the code and @influto/launch_code, and keeps the user ID.
  • Doesn't touch the server, the per-install ID, the checkAccess cache or the list of reported purchases.
  • The next checkAttribution asks the server again. Within the attribution window the server returns the same answer for a device that already claimed a click, so test a fresh referral with a fresh install.

Web-only helpers

@influto/web adds a few methods for runtimes where a referral arrives through a URL.

TypeScript
// captureLaunchCode(url?: string): Promise<LaunchCode | null>
// handleLaunchUrl(url: string): Promise<LaunchCode | null>
// captureInstallReferrer(referrer: string | null | undefined): Promise<LaunchCode | null>
// getLaunchCode(): Promise<LaunchCode | null>
// attachRevenueCat(binding: RevenueCatBinding | null): void
// getInitResult(): InitResult | null
// isInitialized: boolean   (getter)
import InfluTo from '@influto/web';
import { listenForCapacitorLaunchUrls } from '@influto/web/capacitor';

await listenForCapacitorLaunchUrls(InfluTo);          // forwards appUrlOpen + the cold-start URL
await InfluTo.captureInstallReferrer(referrerString); // Android, first launch, before checkAttribution
Helper What it does
captureLaunchCode(url?) Reads influto_code (and influto_click) from a URL — by default the current page — and stores it under @influto/launch_code for checkAttribution. Runs inside initialize unless captureLaunchCode: false. When it reads the current page, it also removes the parameters from the address bar (unless cleanUrl: false). A captured code replaces a stored organic result.
handleLaunchUrl(url) The same, for deep-link handlers (Capacitor appUrlOpen, Cordova custom schemes). listenForCapacitorLaunchUrls(sdk, App?) wires it for you and returns a function that removes the listener.
captureInstallReferrer(referrer) Takes the Google Play Install Referrer string (from @capgo/capacitor-install-referrer or cordova-play-install-referrer). InfluTo's referral links put the code and click ID there, so Android attribution becomes exact. Call it once on first launch, before checkAttribution.
getLaunchCode() The captured launch code, if any.
attachRevenueCat(binding) Sets or replaces the RevenueCat binding after initialize.
getInitResult(), isInitialized The last /sdk/init response, and whether initialize succeeded.
createInfluTo() A separate SDK instance (tests, several apps on one page). Most apps use the default export.

LaunchCode is { code, clickId?, capturedAt }. The root package also exports InfluToApiError, STORAGE_KEYS, SDK_VERSION and the adapter helpers; the React component and hook are documented under UI components.

iOS: appAccountToken(forUserID:)

Returns a stable UUID for a user ID, to pass as the StoreKit appAccountToken when the user buys. Not async, no request.

Swift
// static func appAccountToken(forUserID userID: String) -> UUID
let token = InfluTo.appAccountToken(forUserID: userID)
let outcome = try await product.purchase(options: [.appAccountToken(token)])

The same user ID always gives the same UUID (derived from a SHA-256 of the ID, in UUID version 5 layout), so the purchase stays tied to the user across renewals.

Result types

AttributionResult

Returned by checkAttribution. Same field names on every platform.

Field Type Meaning
attributed boolean Whether a creator referred this install
referralCode string? The creator's code
attributionMethod string? link_param, ip_device_match or manual_entry
clickedAt string? ISO 8601 time of the referral click (or of the manual entry)
message string? Server or SDK explanation, for example "No matching referral click found (organic install)"
clickId string? Web only: the claimed click

CodeValidationResult

Returned by validateCode and applyCode. React Native and web keep snake_case for the nested fields; iOS, Android and Flutter use camelCase.

Field (RN / web) iOS / Android / Flutter Meaning
valid valid Whether the code is an active creator code or a valid free-access code
code code The matched code as stored by InfluTo
campaign.id, .name, .description same The campaign. Free-access codes: name Free Access
campaign.commission_percentage campaign.commissionPercentage Creator commission (0 for free-access codes)
campaign.campaign_type campaign.campaignType public, private, invite_only, or FREE_ACCESS
influencer.name, .social_handle, .follower_count influencer.name, .socialHandle, .followerCount The creator's details — personal data, hidden by default in the UI components. null for free-access codes
custom_data customData Extra data attached to a free-access code
message message Success text; for creator codes it includes the creator's name
error error Error text
error_code errorCode See the table below. Android: enum CodeErrorCode, with UNKNOWN for unrecognized values
applied applied applyCode only: whether the code was applied
Error code When
INVALID_FORMAT Fewer than 4 or more than 20 characters after cleanup
CODE_NOT_FOUND No active creator code or free-access code matches ("Code not found or inactive")
CODE_EXPIRED A free-access code that expired or has no seats left
NETWORK_ERROR Set by the SDK: the request failed, or the SDK isn't initialized

SetCodeResult

Returned by setReferralCode. camelCase on every platform.

Field Type Meaning
success boolean Whether the server recorded the code
code string? The matched code
message string Server or SDK message (optional on iOS, Android and Flutter)
campaign { id, name }? The campaign the code belongs to
freeAccess boolean? true when it's a free-access code
grantsAccess boolean? true when the server granted free access to this user
entitlement string? The RevenueCat entitlement mirrored for the grant, when the app has RevenueCat access set up
expiresAt string? ISO 8601 end of the free access; absent for open-ended access

AccessResult

Returned by checkAccess. camelCase on every platform.

Field Type Meaning
hasAccess boolean Whether InfluTo grants this user free access now
source string? comp when access comes from a free-access code
entitlement string? The mirrored RevenueCat entitlement, if any
expiresAt string? ISO 8601 end of the access; absent when open-ended
code string? The free-access code that granted it

PurchaseResult

Returned by reportPurchase.

Field (RN / web) iOS / Android / Flutter Meaning
success success true when the purchase was verified and processed
validated validated apple or google — a string, not a boolean
environment environment PRODUCTION or SANDBOX
event_type eventType The event recorded, for example INITIAL_PURCHASE or NON_RENEWING_PURCHASE
result result The processing result, for diagnostics
duplicate, recorded — Declared in the web type only (the server always sends them): the purchase was already recorded, or was recorded by this call

PurchaseSyncResult

Returned by the back-sync methods: { fetched, sent, failed } — purchases the store returned, newly reported ones (already-reported purchases count as neither sent nor failed), and reports that threw. iOS and Android add success, which is true when nothing failed.

Campaign

Returned by getActiveCampaigns: id, name, description, and commission_percentage (React Native, web) or commissionPercentage (iOS, Android, Flutter).

Local storage

The SDKs keep a few values on the device, under the same keys on every platform: AsyncStorage on React Native, a UserDefaults suite (com.influto.sdk) on iOS, the influto_prefs SharedPreferences file on Android, shared_preferences on Flutter, and localStorage (or the storage adapter you pass) on web.

Key Holds Cleared by clearAttribution
@influto/attribution The stored attribution result (JSON) Yes
@influto/influto_code The referral or free-access code Yes
@influto/app_user_id The identified user ID Yes (not on web)
@influto/initialized "true" after a successful initialize No
@influto/install_id A random per-install ID, sent with track-install so installs are counted once No
@influto/access The cached positive checkAccess result (5 minutes) No
@influto/launch_code Web only: a code captured from the launch URL or Install Referrer Yes
@influto/reported_purchases React Native, Flutter and web: purchases already reported by automatic capture No
@influto/sent_purchases iOS: purchases already reported by automatic capture No

On Android, android-sdk-billing keeps its list of reported purchases in a separate preferences file, influto_billing_sync_prefs. Uninstalling a native app removes all of these, which is why a reinstall counts as a new install.