Configuration
Complete configuration reference for the Toggly Android SDK.
TogglyConfig
The TogglyConfig class defines all settings for the SDK.
import io.toggly.core.models.TogglyConfig
val config = TogglyConfig(
appKey = "your-app-key",
baseUri = "https://definitions.toggly.io",
environment = "Production",
featureDefaults = mapOf(
"feature-a" to true,
"feature-b" to false
),
refreshInterval = 180_000L,
useSignedDefinitions = false,
verifySignatures = false,
maxSignatureAgeSeconds = null,
connectTimeout = 10_000L,
requestTimeout = 30_000L,
enableLiveUpdates = true
)
Initial groups and claims
Supply known context before initialization so the first evaluated request has the intended user, memberships and rule attributes:
val service = TogglyService(TogglyConfig(
appKey = "your-app-key",
identity = "user-123", // Stable user identifier.
groups = listOf("beta"), // Memberships used by targeting rules.
claims = mapOf("plan" to "pro"), // String rule attributes.
))
service.init() // Run in a coroutine.
Empty groups/claims add no memberships/attributes. Omitted identity retains the stored/generated device ID; explicit empty identity stays empty. Context is copied at construction. Blank groups and empty claim names/values are omitted, with at most 20 claims sent in deterministic order. Use separate group entries, not commas inside group names. Per-check entity context is separate from this user targeting.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
appKey | String? | null | Your Toggly application key |
baseUri | String | "https://definitions.toggly.io" | API base URL |
environment | String | "Production" | Environment name |
featureDefaults | Map<String, Boolean> | emptyMap() | Default feature flag values |
refreshInterval | Long | 180_000L | Auto-refresh interval in milliseconds (0 disables) |
useSignedDefinitions | Boolean | false | Send If-None-Match with the cached ETag on refresh |
verifySignatures | Boolean | false | Verify ES256 signed envelopes via JWKS (SignedDefsVerify) |
maxSignatureAgeSeconds | Long? | null | Reject envelopes older than this many seconds (null / <= 0 disables) |
connectTimeout | Long | 10_000L | Connection timeout in milliseconds |
requestTimeout | Long | 30_000L | Request timeout in milliseconds |
enableLiveUpdates | Boolean | true | Enable WebSocket live updates |
Basic Configuration
Minimal Setup
val config = TogglyConfig(
appKey = "your-app-key"
)
Production Setup
val config = TogglyConfig(
appKey = "your-app-key",
environment = "Production",
refreshInterval = 300_000L, // 5 minutes
useSignedDefinitions = true,
verifySignatures = true
)
Development Setup
val config = TogglyConfig(
appKey = "your-app-key",
environment = "Development",
refreshInterval = 10_000L // Faster refresh for testing
)
Default Flags
Provide fallback values for feature flags when offline or before initialization:
val config = TogglyConfig(
appKey = "your-app-key",
featureDefaults = mapOf(
"new-dashboard" to false,
"beta-feature" to false,
"premium-access" to true
)
)
Default flags are used when:
- The SDK hasn't been initialized yet
- Network requests fail
- A feature key doesn't exist on the server
Signed definitions
The Android client always fetches evaluated-signed definitions:
GET {baseUri}/evaluated-signed/{appKey}/{environment}
Signature verification is controlled separately from conditional requests.
Enable verification (recommended for production)
val config = TogglyConfig(
appKey = "your-app-key",
environment = "Production",
useSignedDefinitions = true, // ETag / If-None-Match on refresh
verifySignatures = true, // ES256 verify via SignedDefsVerify
maxSignatureAgeSeconds = 300L // optional freshness window
)
| Option | Behavior |
|---|---|
useSignedDefinitions | When true and an ETag is cached, refresh sends If-None-Match (HTTP 304 reuses cache). Does not switch the fetch URL. |
verifySignatures | When true, parse the signed envelope, fetch JWKS, and verify with SignedDefsVerify before applying flags. Default false parses the body without crypto checks. |
maxSignatureAgeSeconds | When set to a positive value, reject envelopes whose timestamp is older than this many seconds (or too far in the future). null or <= 0 skips freshness. |
JWKS and verification details
When verifySignatures is enabled:
- Parse the response envelope (
defs/data,signature,timestamp,kid) - Optionally enforce
maxSignatureAgeSeconds - Fetch JWKS from
{baseUri}/.well-known/jwksand persist it in storage - Verify ES256 (ECDSA P-256) over a double SHA-256 digest of
<raw defs json>|<timestamp> - Apply only the verified raw
defsbytes
On failure (invalid envelope, bad signature, unknown kid, stale timestamp), the SDK falls back to cache or featureDefaults and emits an error event — it does not apply the unverified payload.
Cold-start re-verification
With verifySignatures = true, loading a persisted cache:
- Re-verifies using stored signature metadata (
signature,timestamp,kid) and the last JWKS - Clears the cache (fail closed) when signature metadata is missing, the signature is invalid, the
kidis unknown, or freshness fails - Soft-keeps last-known-good flags if JWKS is temporarily unavailable (offline)
Android does not expose an allowedKeyIds allow-list; trust is the JWKS set from /.well-known/jwks.
For the shared evaluated-signed contract, see Evaluated-signed.
Local-Only Mode
Run without connecting to Toggly servers:
val config = TogglyConfig(
appKey = "", // Empty app key
featureDefaults = mapOf(
"feature-a" to true,
"feature-b" to false
)
)
Toggly.configure(
config = config,
storage = MemoryStorage()
)
Auto-Refresh
Enable automatic background refresh of feature flags by setting a non-zero interval:
val config = TogglyConfig(
appKey = "your-app-key",
refreshInterval = 60_000L // 1 minute; 0 disables
)
Refresh Intervals
| Use Case | Recommended Interval |
|---|---|
| Development | 10-30 seconds |
| Staging | 1-2 minutes |
| Production | 5-10 minutes |
| High-traffic apps | 10-15 minutes |
Network Configuration
Timeouts
val config = TogglyConfig(
appKey = "your-app-key",
connectTimeout = 5_000L, // 5 seconds
requestTimeout = 15_000L // 15 seconds
)
Custom Base URL
For self-hosted or enterprise deployments:
val config = TogglyConfig(
appKey = "your-app-key",
baseUri = "https://your-custom-domain.com"
)
Environment-Based Configuration
Using BuildConfig
val config = TogglyConfig(
appKey = BuildConfig.TOGGLY_APP_KEY,
environment = if (BuildConfig.DEBUG) "Development" else "Production",
refreshInterval = if (BuildConfig.DEBUG) 10_000L else 300_000L,
verifySignatures = !BuildConfig.DEBUG
)
Using Gradle Build Variants
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("String", "TOGGLY_APP_KEY", "\"debug-key\"")
buildConfigField("String", "TOGGLY_ENV", "\"Development\"")
}
release {
buildConfigField("String", "TOGGLY_APP_KEY", "\"prod-key\"")
buildConfigField("String", "TOGGLY_ENV", "\"Production\"")
}
}
}
val config = TogglyConfig(
appKey = BuildConfig.TOGGLY_APP_KEY,
environment = BuildConfig.TOGGLY_ENV
)
Configuration with Dependency Injection
Hilt
@Module
@InstallIn(SingletonComponent::class)
object TogglyModule {
@Provides
@Singleton
fun provideTogglyConfig(): TogglyConfig = TogglyConfig(
appKey = BuildConfig.TOGGLY_APP_KEY,
environment = BuildConfig.TOGGLY_ENV
)
@Provides
@Singleton
fun provideTogglyStorage(
@ApplicationContext context: Context
): TogglyStorage = SharedPreferencesStorage(context)
}
Koin
val togglyModule = module {
single {
TogglyConfig(
appKey = BuildConfig.TOGGLY_APP_KEY,
environment = BuildConfig.TOGGLY_ENV
)
}
single<TogglyStorage> {
SharedPreferencesStorage(androidContext())
}
}
Initialization
Application Class
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val config = TogglyConfig(
appKey = "your-app-key",
environment = "Production"
)
Toggly.configure(
config = config,
storage = SharedPreferencesStorage(this)
)
// Initialize in background
CoroutineScope(Dispatchers.IO).launch {
try {
Toggly.shared.init()
} catch (e: Exception) {
Log.e("Toggly", "Initialization failed", e)
}
}
}
}
Lazy Initialization
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Toggly.configure(
config = TogglyConfig(appKey = "your-app-key"),
storage = SharedPreferencesStorage(this)
)
// Don't call init() here - wait until needed
}
}
// Later, when needed
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
if (!Toggly.shared.isInitialized) {
Toggly.shared.init()
}
// Now use feature flags
}
}
}
Reconfiguration
Change configuration at runtime (e.g., after user login):
// Initial anonymous configuration
Toggly.configure(
config = TogglyConfig(appKey = "your-app-key"),
storage = SharedPreferencesStorage(context)
)
Toggly.shared.init()
// After user login, reconfigure with identity
suspend fun onUserLogin(userId: String) {
Toggly.shared.setIdentity(userId)
Toggly.shared.refresh()
}
Best Practices
1. Validate Configuration
fun validateConfig(config: TogglyConfig): Boolean {
return config.appKey.isNotBlank() &&
config.refreshInterval >= 10_000L &&
config.connectTimeout > 0L
}
2. Use Appropriate Timeouts
// Mobile networks can be slow
val config = TogglyConfig(
appKey = "your-app-key",
connectTimeout = 15_000L, // 15 seconds
requestTimeout = 30_000L // 30 seconds
)
3. Handle Configuration Errors
try {
Toggly.configure(config = config, storage = storage)
} catch (e: IllegalArgumentException) {
Log.e("Toggly", "Invalid configuration", e)
// Use fallback or default configuration
}
Next Steps
- Explore Advanced Usage for identity and events
- Check Storage Options for persistence strategies
- See the API Reference for all methods