BetaFlox Documentation
Welcome to the official BetaFlox documentation. Whether you are a developer looking to meet Google Play's testing requirements or a tester looking to earn rewards, you'll find everything you need here.
For Developers
As an indie developer, launching an app on Google Play is fraught with challenges. BetaFlox is specifically designed to solve the two biggest developer pain points: Google Play's rigorous "closed testing" requirements and post-launch app discovery.
The 14-Day Testing Hurdle
Google Play requires developers to have their apps tested in a closed beta test by 12 real testers for 14 consecutive days before the app can be published to production.
Why is this so difficult? Without a tangible reward, friends and generic testers usually abandon the app after a few days and rarely provide constructive criticism. BetaFlox solves this by providing engaged testers bound by an incentivized structure.
Campaign Setup Workflow
1. Create Your Campaign
Start by creating a campaign in the BetaFlox dashboard for the specific application you need tested. Provide the app icon, app name, and exact package name accurately.
2. Draft Mode & IDs
Your campaign will remain in "draft mode" initially. The platform will automatically generate a unique Developer ID and Campaign ID for you.
3. Setup Google Play Closed Beta
Upload your app (with the integrated BetaFlox SDK) to the Google Play Console to create a closed beta test track. Crucially, you must add the BetaFlox Google Group email address to your testers list in the Play Console.
4. Publish
Once Google approves your closed beta release, return to the BetaFlox dashboard and click Publish Campaign. Our testers will immediately be notified and join the 14-day cycle.
The Growth Program Edge
Alternatively, joining the Growth Program gives you access to an expanded pool of testers, allowing your app to achieve 100 or 200 real app downloads.
More than just meeting the Google Play 14-day requirement, the higher user volume ensures genuine engagement for a month or more. This influx of engagement signals positive velocity to Google Play's discovery algorithm right out of the gate.
Android SDK Integration Guide
Ready-to-Use AI Agent Prompt (Android)
Copy & paste into Antigravity, Claude, ChatGPT, or Cursor to auto-integrate
Please integrate the BetaFlox SDK into this native Android project using the Developer API Key and Campaign ID provided below:
1. Repository Configuration (settings.gradle.kts or root build.gradle.kts):
- Ensure the JitPack repository is present in dependencyResolutionManagement.repositories:
maven { url = uri("https://jitpack.io") }
2. Add Dependency (app/build.gradle.kts):
- Add the BetaFlox SDK dependency to dependencies block:
implementation("com.github.skihal43:BetaFlox-SDK:1.0.23")
3. Android Manifest & Jetifier (AndroidManifest.xml & gradle.properties):
- Register your custom Application class in AndroidManifest.xml: <application android:name=".InitApp">
- Ensure AndroidX and Jetifier are enabled in gradle.properties:
android.useAndroidX=true
android.enableJetifier=true
4. Initialize SDK in Application Class (InitApp.kt):
- Import com.betaflox.sdk.BetaFloxSDK
- Initialize BetaFloxSDK inside Application.onCreate():
package com.temtem.app
import android.app.Application
import com.betaflox.sdk.BetaFloxSDK
class InitApp : Application() {
override fun onCreate() {
super.onCreate()
BetaFloxSDK.initialize(
context = this,
apiKey = "YOUR_DEVELOPER_API_KEY",
campaignId = "YOUR_CAMPAIGN_ID"
)
}
}
Note: Replace "YOUR_DEVELOPER_API_KEY" and "YOUR_CAMPAIGN_ID" with your actual Developer API Key and Campaign ID from the BetaFlox Dashboard.Configure Repository
In your application's settings.gradle.kts (or root build.gradle.kts), ensure JitPack is added to your dependency resolution management:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}JitPack Repository Line
maven { url = uri("https://jitpack.io") }Add Dependency
Add the dependency to your app module's build configuration:
dependencies {
implementation("com.github.skihal43:BetaFlox-SDK:1.0.23")
}SDK Dependency Line
implementation("com.github.skihal43:BetaFlox-SDK:1.0.23")Initialization
Initialize the SDK in your custom Application class. No manual lifecycle tracking required!
import com.betaflox.sdk.BetaFloxSDK
class InitApp : Application() {
override fun onCreate() {
super.onCreate()
BetaFloxSDK.initialize(
context = this,
apiKey = "YOUR_API_KEY",
campaignId = "YOUR_CAMPAIGN_ID"
)
}
}SDK Import
import com.betaflox.sdk.BetaFloxSDKSDK Initialization
BetaFloxSDK.initialize(
context = this,
apiKey = "YOUR_API_KEY",
campaignId = "YOUR_CAMPAIGN_ID"
)Manifest Merge Conflicts
If you encounter a manifest merge error (`Attribute application@appComponentFactory`), it means your project needs to migrate older support libraries to AndroidX using Jetifier.
Enable Jetifier in your gradle.properties:
android.useAndroidX=true
# Enable Jetifier to migrate 3rd party libraries
android.enableJetifier=trueAndroidX Setting
android.useAndroidX=trueJetifier Setting
android.enableJetifier=trueFlutter SDK Integration Guide
Seamlessly integrate the BetaFlox SDK into any Flutter project with zero logo freeze, accurate session tracking, zero data corruption, and maximum performance across all Android devices (including Huawei/EMUI).
Analysis of Potential Integration Issues & Solutions
Logo Screen Freeze (8–19s Hang)
PreventedSynchronous Play Integrity initialization during process start or ContentProvider IPC calls on main thread to frozen companion app.
BetaFloxSDK.initialize() until onFlutterUiDisplayed() and pre-inject tester ID into launch intent.Missing sdk_events (0 Heartbeats)
PreventedInitializing SDK after Android lifecycle events causes the internal lifecycle tracker to miss the initial foreground transition.
onActivityStarted & onActivityResumed via reflection after post-render initialization.Tester ID Mismatches
PreventedAsync device-hash resolution queries overwrite valid tester ID with an old/anonymous ID 500ms after boot.
testerId pre-binding before starting lifecycle timers with delayed override protection.Unbound Launch Pollution
PreventedUnbound user opening app directly creates anonymous session documents and pollutes Firestore database.
betaflox_tester_id is present in extras/prefs.SDK Stops Sending Events After Switching to BetaFlox App ⚠️
PreventedSwitching to BetaFlox companion app fires onActivityStopped on the SDK lifecycle tracker, permanently stopping heartbeats because resume-from-background only fires onActivityResumed (not onActivityStarted).
onResume() to call reactivateSdkLifecycleTracker() which manually fires onActivityStarted + onActivityResumed on the tracker.Simple 5-Step Integration Summary
Step 1: Add Repository & Dependency
Add JitPack to android/settings.gradle.kts and add implementation("com.github.skihal43:BetaFlox-SDK:1.0.23") to android/app/build.gradle.kts.
Step 2: Configure AndroidManifest.xml
Set android:name=".InitApp" on <application> and add deep link intent filters (betaflox:// and https://betaflox.app/join).
Step 3: Create Clean InitApp.kt
Create InitApp.kt extending Application. Keep onCreate() 100% clean with zero SDK calls so cold boot is instant (<20ms).
Step 4: Create MainActivity.kt & BetaFloxChannelHost.kt
Defer SDK initialization to onFlutterUiDisplayed(), activate lifecycleTracker manually when bound to valid tester ID, override onResume() to reactivate tracker after app switching, and register Method Channel bridge com.yourcompany.yourapp/betaflox.
Step 5: Add betaflox_service.dart & Bind Login
Add BetaFloxService in Dart and call BetaFloxService.instance.setTesterId(user.uid) when the user logs in.
Ready-to-Use AI Agent Prompt
Copy & paste into Antigravity, Claude, ChatGPT, or Cursor to auto-integrate
Please integrate the BetaFlox SDK into this Flutter project following these exact technical rules to prevent logo screen freezes, process hangs, tester ID mismatches, and SDK heartbeat interruptions when switching between apps:
1. Dependencies:
- Ensure JitPack repository (`maven { url = uri("https://jitpack.io") }`) is present in `android/settings.gradle.kts`.
- Add `implementation("com.github.skihal43:BetaFlox-SDK:1.0.23")` in `android/app/build.gradle.kts`.
2. Android Manifest (`android/app/src/main/AndroidManifest.xml`):
- Set `android:name=".InitApp"` in `<application>`.
- Add intent filters for `betaflox://` custom scheme and `https://betaflox.app/join` URLs.
3. Deferred Initialization (`InitApp.kt`):
- Create `InitApp : Application()`. Keep `onCreate()` completely free of SDK calls so Android process startup is 100% clean (<20ms).
4. Post-Render SDK Hook (`MainActivity.kt`):
- Override `onFlutterUiDisplayed()`. Initialize `BetaFloxSDK` inside a background coroutine AFTER Flutter has rendered its first frame on screen.
- Resolve `validTesterId` from launch intent extras, deep links, or saved preferences.
- If a valid `testerId` exists: initialize SDK, call `BetaFloxSDK.setTesterId(validTesterId)`, and manually notify `lifecycleTracker` (`onActivityStarted` & `onActivityResumed`) via reflection so heartbeat timers start logging `sdk_events`.
- If no valid `testerId` exists, keep the SDK dormant to prevent anonymous Firestore writes.
- IMPORTANT: Override `onResume()` to call `reactivateSdkLifecycleTracker()` every time the user returns to the app. This fixes the issue where switching to the BetaFlox companion app stops the SDK's heartbeat manager permanently. The helper must manually call `onActivityStarted` and `onActivityResumed` on the SDK's internal lifecycle tracker via reflection.
5. Method Channel Host (`BetaFloxChannelHost.kt`):
- Register MethodChannel `com.yourcompany.yourapp/betaflox` and EventChannel `com.yourcompany.yourapp/betaflox_verification`.
- Implement `setTesterId` to save `tester_id` in `betaflox_prefs` SharedPreferences and trigger `BetaFloxSDK.syncNow()`.
6. Dart Service (`lib/core/services/betaflox_service.dart`):
- Create singleton service exposing `setTesterId(uid)`, `getTesterId()`, `syncNow()`, and `verificationStateStream`.
- Call `BetaFloxService.instance.setTesterId(user.uid)` in your authentication flow whenever a user logs in or resumes session.Complete Ready-to-Copy Source Code Files
Click "Extend Code View" to expand long filespackage com.temtem.app
import android.app.Application
import android.util.Log
import kotlinx.coroutines.CompletableDeferred
class InitApp : Application() {
companion object {
val sdkReady = CompletableDeferred<Unit>()
}
override fun onCreate() {
super.onCreate()
Log.i("InitApp", "Application startup clean (SDK initialization deferred to post-Flutter UI display).")
}
}package com.temtem.app
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import com.betaflox.sdk.BetaFloxSDK
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainActivity : FlutterActivity() {
private var betaFloxHost: BetaFloxChannelHost? = null
override fun onCreate(savedInstanceState: Bundle?) {
injectTesterIdIntoIntent(intent)
super.onCreate(savedInstanceState)
}
override fun onFlutterUiDisplayed() {
super.onFlutterUiDisplayed()
Log.i("BFX", "onFlutterUiDisplayed: Flutter UI rendered!")
CoroutineScope(Dispatchers.IO).launch {
try {
val validTesterId = resolveValidTesterId(intent)
if (validTesterId != null) {
val prefs = getSharedPreferences("betaflox_prefs", Context.MODE_PRIVATE)
prefs.edit().putString("tester_id", validTesterId).apply()
if (!InitApp.sdkReady.isCompleted) {
BetaFloxSDK.initialize(
context = applicationContext,
apiKey = "YOUR_API_KEY",
campaignId = "YOUR_CAMPAIGN_ID"
)
InitApp.sdkReady.complete(Unit)
}
BetaFloxSDK.setTesterId(validTesterId)
intent.putExtra("betaflox_tester_id", validTesterId)
// Manually notify lifecycleTracker so heartbeatManager starts logging sdk_events
try {
val field = BetaFloxSDK::class.java.getDeclaredField("lifecycleTracker")
field.isAccessible = true
val tracker = field.get(BetaFloxSDK) as? android.app.Application.ActivityLifecycleCallbacks
tracker?.onActivityStarted(this@MainActivity)
tracker?.onActivityResumed(this@MainActivity)
} catch (_: Exception) {}
BetaFloxSDK.handleLaunchIntent(intent)
BetaFloxSDK.handleDeepLink(intent)
} else {
Log.i("BFX", "Direct launch before BetaFlox binding -> SDK remains dormant.")
}
} catch (e: Exception) {
Log.e("BFX", "onFlutterUiDisplayed error: ${e.message}", e)
}
}
tryBindFromClipboard()
}
override fun onResume() {
super.onResume()
// Re-activate SDK lifecycle tracker each time the user returns to the app.
// Without this, switching to the BetaFlox companion app fires onActivityStopped
// on the SDK tracker, permanently stopping heartbeats. Calling reactivateSdkLifecycleTracker()
// here ensures sdk_events resume immediately every time the user comes back.
reactivateSdkLifecycleTracker()
tryBindFromClipboard()
}
override fun onStop() {
super.onStop()
Log.i("BFX", "onStop: User switched away (e.g., to BetaFlox). Heartbeats will resume on return.")
}
/**
* Re-activates the SDK's internal AppLifecycleTracker.
*
* Problem (Issue #5): When the user switches from the tested app to the BetaFlox companion
* app (or any other app), Android fires onActivityStopped on the SDK's registered
* ActivityLifecycleCallbacks, which calls heartbeatManager.stop() + sessionManager.endSession().
* When the user returns, Android only fires onActivityResumed — but heartbeatManager.start()
* is only triggered from onActivityStarted, which is never re-fired for a resume-from-background.
*
* Solution: Manually invoke onActivityStarted + onActivityResumed on the tracker on every
* onResume() so heartbeats restart immediately whenever the user returns to the app.
*/
private fun reactivateSdkLifecycleTracker() {
if (!InitApp.sdkReady.isCompleted) return
val boundId = resolveValidTesterId(intent)
if (boundId.isNullOrBlank()) return
CoroutineScope(Dispatchers.IO).launch {
try {
val field = BetaFloxSDK::class.java.getDeclaredField("lifecycleTracker")
field.isAccessible = true
val tracker = field.get(BetaFloxSDK) as? android.app.Application.ActivityLifecycleCallbacks
tracker?.onActivityStarted(this@MainActivity)
tracker?.onActivityResumed(this@MainActivity)
Log.i("BFX", "reactivateSdkLifecycleTracker: heartbeats resumed for testerId=${BetaFloxSDK.getTesterId()}")
} catch (e: Exception) {
Log.w("BFX", "reactivateSdkLifecycleTracker: ${e.message}")
}
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
betaFloxHost = BetaFloxChannelHost(flutterEngine, applicationContext).apply { register() }
}
private fun resolveValidTesterId(intent: Intent?): String? {
if (intent != null && intent.hasExtra("betaflox_tester_id")) {
val extraId = intent.getStringExtra("betaflox_tester_id")
if (!extraId.isNullOrBlank() && extraId != "pending_binding") return extraId
}
val prefs = getSharedPreferences("betaflox_prefs", Context.MODE_PRIVATE)
val storedId = prefs.getString("tester_id", null)
if (!storedId.isNullOrBlank() && storedId != "pending_binding") return storedId
return null
}
private fun injectTesterIdIntoIntent(intent: Intent?) {
if (intent == null) return
val validId = resolveValidTesterId(intent)
if (!validId.isNullOrBlank()) intent.putExtra("betaflox_tester_id", validId)
}
private fun tryBindFromClipboard() {
CoroutineScope(Dispatchers.IO).launch {
try {
InitApp.sdkReady.await()
if (!BetaFloxSDK.getTesterId().isNullOrBlank()) return@launch
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return@launch
val text = clipboard.primaryClip?.getItemAt(0)?.coerceToText(this@MainActivity)?.toString()?.trim() ?: return@launch
if (!text.contains("token=")) return@launch
BetaFloxSDK.handleDeepLink(Intent(Intent.ACTION_VIEW, Uri.parse(text)))
} catch (_: Exception) {}
}
}
}package com.temtem.app
import android.util.Log
import com.betaflox.sdk.BetaFloxSDK
import com.betaflox.sdk.VerificationState
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class BetaFloxChannelHost(
private val flutterEngine: FlutterEngine,
private val context: android.content.Context
) {
companion object {
const val CHANNEL = "com.yourcompany.yourapp/betaflox"
const val EVENT_CHANNEL = "com.yourcompany.yourapp/betaflox_verification"
}
private var verificationSink: EventChannel.EventSink? = null
fun register() {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result -> handle(call, result) }
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
verificationSink = events
collectVerificationState()
}
override fun onCancel(arguments: Any?) { verificationSink = null }
})
}
private fun collectVerificationState() {
CoroutineScope(Dispatchers.Main).launch {
try {
BetaFloxSDK.verificationState.collectLatest { state ->
val map = when (state) {
is VerificationState.Pending -> mapOf("state" to "pending")
is VerificationState.Complete -> mapOf("state" to "complete", "verdict" to state.verdict.verdict, "riskScore" to state.verdict.riskScore)
is VerificationState.Failed -> mapOf("state" to "failed", "reason" to state.reason)
}
verificationSink?.success(map)
}
} catch (_: Exception) {}
}
}
private fun handle(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"initialize" -> {
val apiKey = call.argument<String>("apiKey") ?: "YOUR_API_KEY"
val campaignId = call.argument<String>("campaignId") ?: "YOUR_CAMPAIGN_ID"
CoroutineScope(Dispatchers.IO).launch {
try {
BetaFloxSDK.initialize(context.applicationContext, apiKey, campaignId)
InitApp.sdkReady.complete(Unit)
withContext(Dispatchers.Main) { result.success(null) }
} catch (e: Exception) {
withContext(Dispatchers.Main) { result.error("INIT_FAILED", e.message, null) }
}
}
}
"setTesterId" -> {
val id = call.argument<String>("testerId") ?: return result.error("ARGS", "testerId required", null)
if (id.isNotBlank() && id != "pending_binding") {
CoroutineScope(Dispatchers.IO).launch {
try {
if (!InitApp.sdkReady.isCompleted) {
BetaFloxSDK.initialize(context.applicationContext, "YOUR_API_KEY", "YOUR_CAMPAIGN_ID")
InitApp.sdkReady.complete(Unit)
}
BetaFloxSDK.setTesterId(id)
context.getSharedPreferences("betaflox_prefs", android.content.Context.MODE_PRIVATE).edit().putString("tester_id", id).apply()
BetaFloxSDK.syncNow()
withContext(Dispatchers.Main) { result.success(null) }
} catch (e: Exception) {
withContext(Dispatchers.Main) { result.error("SET_TESTER_FAILED", e.message, null) }
}
}
} else {
result.success(null)
}
}
"getTesterId" -> result.success(BetaFloxSDK.getTesterId())
"syncNow" -> {
CoroutineScope(Dispatchers.IO).launch {
try {
BetaFloxSDK.syncNow()
withContext(Dispatchers.Main) { result.success(null) }
} catch (e: Exception) {
withContext(Dispatchers.Main) { result.error("SYNC_FAILED", e.message, null) }
}
}
}
else -> result.notImplemented()
}
}
}import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
sealed class VerificationState {
const VerificationState();
}
class VerificationPending extends VerificationState {
const VerificationPending();
}
class VerificationComplete extends VerificationState {
final String verdict;
final double riskScore;
const VerificationComplete({required this.verdict, required this.riskScore});
}
class VerificationFailed extends VerificationState {
final String reason;
const VerificationFailed({required this.reason});
}
class BetaFloxService {
static const MethodChannel _channel = MethodChannel('com.yourcompany.yourapp/betaflox');
static const EventChannel _eventChannel = EventChannel('com.yourcompany.yourapp/betaflox_verification');
static final BetaFloxService instance = BetaFloxService._();
BetaFloxService._();
Stream<VerificationState>? _verificationStream;
Future<void> setTesterId(String testerId) async {
try {
await _channel.invokeMethod('setTesterId', {'testerId': testerId});
} on PlatformException catch (e) {
debugPrint('BetaFlox setTesterId error: ${e.message}');
}
}
Future<String?> getTesterId() async {
try {
return await _channel.invokeMethod<String>('getTesterId');
} on PlatformException catch (e) {
return null;
}
}
Future<void> syncNow() async {
try {
await _channel.invokeMethod('syncNow');
} on PlatformException catch (_) {}
}
Stream<VerificationState> get verificationStateStream {
_verificationStream ??= _eventChannel.receiveBroadcastStream().map((dynamic event) {
final map = Map<String, dynamic>.from(event as Map);
final stateStr = map['state'] as String?;
return switch (stateStr) {
'complete' => VerificationComplete(
verdict: (map['verdict'] as String?) ?? 'VALID',
riskScore: ((map['riskScore'] as num?) ?? 0.0).toDouble(),
),
'failed' => VerificationFailed(
reason: (map['reason'] as String?) ?? 'Unknown error',
),
_ => const VerificationPending(),
};
});
return _verificationStream!;
}
}Unity SDK Integration
Welcome to the BetaFlox SDK integration guide for Unity! This tutorial will walk you through the process of adding the BetaFlox SDK to your Unity game or application.
Required: The minimum API Level is 24 for the Unity BetaFlox SDK to work without errors.
Import the SDK into Unity
- Open your project in the Unity Editor (2021.3 LTS or newer recommended).
- In the top menu, navigate to Assets > Import Package > Custom Package...
- Locate and select the downloaded
BetaFloxSDK.unitypackage. - The Import Unity Package window will appear. Leave all files selected and click Import.
This will add the BetaFlox folder to your Assets directory, containing the necessary C# wrappers, native plugins, and editor scripts.
Resolve Android Dependencies
The BetaFlox SDK relies on certain native Android libraries. To ensure these are correctly included in your build, use the External Dependency Manager for Unity (EDM4U):
- Go to Assets > External Dependency Manager > Android Resolver > Resolve.
- Wait for the resolution process to complete.
Initialize the BetaFlox SDK
You must initialize the SDK before using any of its features. The easiest way is using the built-in BetaFloxManager component.
- Create an empty GameObject in your first scene (e.g., name it
[BetaFlox]). - Add the BetaFloxManager component to it via Add Component → Scripts → BetaFloxManager.
- In the Inspector, fill in your Developer ID and Campaign ID from the BetaFlox Dashboard.
Alternatively, initialize it in code:
using BetaFlox;
var config = new BetaFloxConfig.Builder("YOUR_DEVELOPER_ID", "YOUR_CAMPAIGN_ID")
.SetLogLevel(BetaFloxLogLevel.Debug)
.Build();
BetaFloxSDK.Initialize(config);Test the Integration
- Build your project for Android (File > Build Settings > Android > Build and Run).
- Check the Unity logcat output for the success message:
BetaFlox SDK Initialized Successfully!
If initialization fails, ensure your Developer ID and Campaign ID are correct and that the device has internet access.
For Testers
As a BetaFlox Tester, you get paid to help fix applications. The process requires commitment and consistency.
The Commitment
You must test the application for at least 4 continuous minutes every single day for 14 consecutive days. Missing even one day instantly disqualifies you from the campaign.
Reward Structure
Earn 10 points for every day you complete the test. Furthermore, earn an extra 2 points for every helpful, constructive comment you submit to the developer.
Strict Rules & Anti-Fraud
We guarantee developers real, actionable feedback. Therefore, any attempt to farm rewards or automate testing will result in an immediate and permanent ban.
No VPNs or Proxies
Using a VPN to mask location, claim points across regions, or bypass daily limits falsifies data and violates our terms.
No Emulators
Apps must be tested on real mobile devices to provide real performance data. BlueStacks, Nox Player, etc. are strictly prohibited.
No Multiple Accounts
You are permitted one BetaFlox account per person and per device. Bot-netting alt accounts triggers immediate flagging.
No Auto-Clickers
Scripting gameplay or automating daily 4-minute sessions ruins test integrity. Our SDK detects machine-like interactions instantly.
No Spam Feedback
Providing gibberish, copy-pasting Play Store reviews, or giving single-word "good app" responses to get points will result in rejection.
The Golden Rule
Test exactly as a normal user would, provide genuine and actionable feedback, check in daily for 14 days, and you will thrive securely on BetaFlox.