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
initializeandreportPurchasethrow on network or server errors. Everything else returns a safe value (attributed: false,[],valid: false,success: false,hasAccess: false) and logs the error whendebugis on. - Before
initializesucceeds, methods return their fail-soft values without a request.checkAttributionandreportPurchasethrow instead (on iOS,checkAttributionreturnsattributed: falsewith the messageSDK not initialized). On React Native,enableAutoPurchaseCaptureandsyncExistingPurchasesalso 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
asyncstatics onInfluTo; Flutter usesInfluTo.instanceand Futures; Android methods aresuspendfunctions onobject InfluTo, and every network method also has an overload whose last parameter is a(Result<T>) -> Unitcallback, 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.
// initialize(config: InfluToConfig): Promise<void>
import InfluTo from '@influto/react-native-sdk';
await InfluTo.initialize({ apiKey: 'it_…', debug: __DEV__, appVersion: '1.4.0' });// static func initialize(_ config: InfluToConfig) async throws
import InfluTo
try await InfluTo.initialize(InfluToConfig(apiKey: "it_…", debug: true, appVersion: "1.4.0"))// 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))
}// Future<void> initialize(InfluToConfig config)
import 'package:influto/influto.dart';
await InfluTo.instance.initialize(const InfluToConfig(apiKey: 'it_…', appVersion: '1.4.0'));// 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) andautoCaptureisn'tfalse, 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/initresponse asInitResult(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.
// checkAttribution(): Promise<AttributionResult>
const attribution = await InfluTo.checkAttribution();
if (attribution.attributed) console.log(attribution.referralCode);// static func checkAttribution() async -> AttributionResult
let attribution = await InfluTo.checkAttribution()
if attribution.attributed, let code = attribution.referralCode { print(code) }// suspend fun checkAttribution(): AttributionResult
val attribution = InfluTo.checkAttribution()
if (attribution.attributed) Log.d("Referral", attribution.referralCode ?: "")// Future<AttributionResult> checkAttribution()
final attribution = await InfluTo.instance.checkAttribution();
if (attribution.attributed) debugPrint(attribution.referralCode);// 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_codeand the SDK sets the RevenueCat attributesinfluto_code= the code andinfluto_referral="true"(a string). That happens automatically on React Native (withreact-native-purchasesinstalled) and Android; iOS, Flutter and web need the setter, hook or binding frominitialize. attributionMethodislink_param(a link carried the code into the app),ip_device_match(link-click matching) ormanual_entry(a code applied withsetReferralCode). 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
initializesucceeded, 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).
// identifyUser(appUserId: string, properties?: Record<string, any>): Promise<void>
await InfluTo.identifyUser(userId, { plan: 'free' });// static func identifyUser(_ appUserId: String, properties: [String: AnyCodable]? = nil) async
await InfluTo.identifyUser(userID, properties: ["plan": AnyCodable("free")])// suspend fun identifyUser(appUserId: String, properties: Map<String, Any?>? = null)
InfluTo.identifyUser(userId, mapOf("plan" to "free"))// Future<void> identifyUser(String appUserId, {Map<String, dynamic>? properties})
await InfluTo.instance.identifyUser(userId, properties: {'plan': 'free'});// 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).checkAccessandreportPurchaseuse 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_codeattribute, 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).
// 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' } });// 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")]))// 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"))// 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'}));// 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: fortrial_started,subscription_purchasedandsubscription_renewedit'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 owneventIdto 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.
// getActiveCampaigns(): Promise<Campaign[]>
const campaigns = await InfluTo.getActiveCampaigns();// static func getActiveCampaigns() async -> [Campaign]
let campaigns = await InfluTo.getActiveCampaigns()// suspend fun getActiveCampaigns(): List<Campaign>
val campaigns = InfluTo.getActiveCampaigns()// Future<List<Campaign>> getActiveCampaigns()
final campaigns = await InfluTo.instance.getActiveCampaigns();// 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.
// getReferralCode(): Promise<string | null>
const code = await InfluTo.getReferralCode();// static func getReferralCode() async -> String?
let code = await InfluTo.getReferralCode()// fun getReferralCode(): String? (not suspend)
val code = InfluTo.getReferralCode()// Future<String?> getReferralCode()
final code = await InfluTo.instance.getReferralCode();// 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.
// getPrefilledCode(): Promise<string | null>
const prefill = await InfluTo.getPrefilledCode();// static func getPrefilledCode() async -> String?
let prefill = await InfluTo.getPrefilledCode()// fun getPrefilledCode(): String? (not suspend)
val prefill = InfluTo.getPrefilledCode()// Future<String?> getPrefilledCode()
final prefill = await InfluTo.instance.getPrefilledCode();// 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.
// validateCode(code: string): Promise<CodeValidationResult>
const check = await InfluTo.validateCode(input);
if (!check.valid) showError(check.error_code);// static func validateCode(_ code: String) async -> CodeValidationResult
let check = await InfluTo.validateCode(input)
if !check.valid { print(check.errorCode ?? "") }// suspend fun validateCode(code: String): CodeValidationResult
val check = InfluTo.validateCode(input)
if (!check.valid) Log.d("Code", check.errorCode?.name ?: "")// Future<CodeValidationResult> validateCode(String code)
final check = await InfluTo.instance.validateCode(input);
if (!check.valid) debugPrint(check.errorCode);// 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_typeFREE_ACCESSand the campaign nameFree Access. - For creator codes,
influencer.nameand themessagetext ("Valid code! Referred by …") contain the creator's personal name. Don't show them unless your creators agreed. - Fail-soft: before
initializeor 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.
// setReferralCode(code: string, appUserId?: string): Promise<SetCodeResult>
const result = await InfluTo.setReferralCode(code, userId);
if (result.grantsAccess) unlockPremium();// static func setReferralCode(_ code: String, appUserId: String? = nil) async -> SetCodeResult
let result = await InfluTo.setReferralCode(code, appUserId: userID)
if result.grantsAccess == true { unlockPremium() }// suspend fun setReferralCode(code: String, appUserId: String? = null): SetCodeResult
val result = InfluTo.setReferralCode(code, userId)
if (result.grantsAccess == true) unlockPremium()// Future<SetCodeResult> setReferralCode(String code, {String? appUserId})
final result = await InfluTo.instance.setReferralCode(code, appUserId: userId);
if (result.grantsAccess == true) unlockPremium();// 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,applyCodevalidates first and returns readable error codes. - Free-access codes need a user ID.
appUserIddefaults to the user fromidentifyUser(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 answerssuccess: false("Could not identify your account"). iOS, Android and Flutter also store a passedappUserIdas 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: truewith the message "Code already attributed from link click" and records nothing new. - An unknown or inactive code is a
404from the server, which the SDK returns assuccess: false. - Gate premium on
checkAccess(or the store entitlement);grantsAccessis for unlocking immediately.
applyCode
The usual call for a typed code: runs validateCode, and if the code is valid, setReferralCode.
// 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();
}// 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() }
}// 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()
}// 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();
}// 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:truewhensetReferralCodesucceeded,falsewhen 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, checkcampaign_type/campaignTypeforFREE_ACCESSand then callcheckAccess, as above — or callvalidateCodeandsetReferralCodeyourself and readgrantsAccess.
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.
// checkAccess(appUserId?: string): Promise<AccessResult>
const premium = entitlementActive || (await InfluTo.checkAccess(userId)).hasAccess;// static func checkAccess(appUserId: String? = nil) async -> AccessResult
let access = await InfluTo.checkAccess(appUserId: userID)
let premium = entitlementActive || access.hasAccess// suspend fun checkAccess(appUserId: String? = null): AccessResult
val premium = entitlementActive || InfluTo.checkAccess(userId).hasAccess// Future<AccessResult> checkAccess({String? appUserId})
final premium = entitlementActive || (await InfluTo.instance.checkAccess(appUserId: userId)).hasAccess;// checkAccess(appUserId?: string): Promise<AccessResult>
const premium = entitlementActive || (await InfluTo.checkAccess(userId)).hasAccess;Returns an AccessResult.
- Uses
appUserId, or the ID stored byidentifyUser/setReferralCode. With no user ID it returnshasAccess: falsewithout 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.clearAttributiondoesn'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.
// 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// 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
}// 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// 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);// 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:
400while the platform isn't switched to store-direct (so it's safe to ship first) or when the proof fails verification,409for an Android one-time purchase that isn't in the PURCHASED state yet,503when 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 toproduct.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() |
// enableAutoPurchaseCapture(opts?: { backSync?: boolean; oneTimeProductIds?: string[] }): Promise<PurchaseSyncResult>
// disableAutoPurchaseCapture(): void
// syncExistingPurchases(): Promise<PurchaseSyncResult>
const { fetched, sent, failed } = await InfluTo.syncExistingPurchases();// static func startPurchaseObservation() async
// static func stopPurchaseObservation() async
// @discardableResult static func syncExistingPurchases() async -> PurchaseSyncResult
let sync = await InfluTo.syncExistingPurchases()// 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)// 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();// 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 aPurchasefrom your ownBillingClientwithout a second client; it returnstruewhen the purchase was newly reported.startObservationruns its ownBillingClient— use it only when nothing else handles billing. - Flutter: the observer checks
InfluTo.instance.autoCaptureEnabled, so callenable()afterinitializehas finished. - Web purchase sources:
capgoNativePurchasesSource(NativePurchases, opts?)from@influto/web/capacitor,cordovaPurchaseSource(store, opts?)from@influto/web/cordova, anddigitalGoodsSource(opts?)from@influto/web/pwafor Google Play in a Trusted Web Activity. The Digital Goods API has no purchase event, so callsyncExistingPurchases()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.
// clearAttribution(): Promise<void>
await InfluTo.clearAttribution();// static func clearAttribution() async
await InfluTo.clearAttribution()// fun clearAttribution() (not suspend)
InfluTo.clearAttribution()// Future<void> clearAttribution()
await InfluTo.instance.clearAttribution();// clearAttribution(): Promise<void>
await InfluTo.clearAttribution();- Removes
@influto/attribution,@influto/influto_codeand@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
checkAccesscache or the list of reported purchases. - The next
checkAttributionasks 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.
// 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.
// 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.