Return Types & Models
Kotlin data classes, enums, and feature-variable accessors
This page documents the public Kotlin types the SDK returns and the enums you pass in. All model types live in com.convert.sdk.core.model; the event constants live in com.convert.sdk.core.event. The SDK never throws from its public decision methods — absent or not-ready data surfaces as null or an empty list.
Variation
VariationReturned by ConvertContext.runExperience(...) (nullable) and runExperiences(...) (as a list). A Kotlin data class with all-nullable fields:
public data class Variation(
val id: String? = null,
val key: String? = null,
val name: String? = null,
val experienceId: String? = null,
val experienceKey: String? = null,
val experienceName: String? = null,
val bucketingAllocation: Double? = null,
val changes: List<JsonElement>? = null,
)key— the merchant-defined variation key from the Convert dashboard; this is what you branch on.id— the variation identifier.bucketingAllocation— the basis-point allocation the visitor hit. Populated on fresh bucketing;nullon a sticky lookup.changes— raw variation changes, asJsonElementvalues.
val variation = ctx.runExperience("homepage-redesign")
when (variation?.key) {
"control" -> renderControl()
"treatment" -> renderTreatment()
null -> renderControl() // not ready / not bucketed / experience absent
}Feature and FeatureStatus
Feature and FeatureStatusReturned by ConvertContext.runFeature(...) (nullable) and runFeatures(...) (as a list):
public data class Feature(
val id: String? = null,
val key: String? = null,
val name: String? = null,
val status: FeatureStatus,
val variables: Map<String, JsonElement>? = null,
val experienceId: String? = null,
val experienceKey: String? = null,
val experienceName: String? = null,
) {
val enabled: Boolean get() = status == FeatureStatus.ENABLED
}
public enum class FeatureStatus { ENABLED, DISABLED }enabledis a convenience forstatus == FeatureStatus.ENABLED.- When a feature is
DISABLED,variablesisnulland the accessors below returnnull.
val feature = ctx.runFeature("checkout-v2")
if (feature?.enabled == true) {
enableCheckoutV2()
}Feature variable accessors
When a feature is ENABLED, variables holds JsonElement values — usually JsonPrimitive scalars. For the common scalar case, use the extension helpers in com.convert.sdk.android (file facade FeatureExtensions):
import com.convert.sdk.android.getString
import com.convert.sdk.android.getInt
import com.convert.sdk.android.getDouble
import com.convert.sdk.android.getBoolean
val color = feature?.getString("ctaColor") ?: "#0066ff"
val limit = feature?.getInt("maxItems") ?: 20
val price = feature?.getDouble("price") ?: 9.99
val experimental = feature?.getBoolean("experimental") == trueCoercion is strict-then-loose: numeric primitives resolve directly; numeric strings fall back through toXxxOrNull(). JsonNull, arrays, nested objects, and absent keys all return null. For structured (JsonObject / JsonArray) variables, read feature.variables?.get("key") and decode the JsonElement yourself with kotlinx.serialization.
GoalData and GoalDataKey
GoalData and GoalDataKeyPassed to ConvertContext.trackConversion(...) to attach transactional data to a conversion:
public data class GoalData(
val key: GoalDataKey? = null,
val value: JsonElement? = null,
)GoalDataKey values and their JSON names on the wire:
| Enum value | JSON name | Typical value |
|---|---|---|
AMOUNT | amount | number |
PRODUCTS_COUNT | productsCount | integer |
TRANSACTION_ID | transactionId | string |
CUSTOM_DIMENSION_1 | customDimension1 | string / number |
CUSTOM_DIMENSION_2 | customDimension2 | string / number |
CUSTOM_DIMENSION_3 | customDimension3 | string / number |
CUSTOM_DIMENSION_4 | customDimension4 | string / number |
CUSTOM_DIMENSION_5 | customDimension5 | string / number |
import com.convert.sdk.core.model.GoalData
import com.convert.sdk.core.model.GoalDataKey
import kotlinx.serialization.json.JsonPrimitive
ctx.trackConversion(
goalKey = "purchase-completed",
goalData = listOf(
GoalData(GoalDataKey.AMOUNT, JsonPrimitive(49.99)),
GoalData(GoalDataKey.PRODUCTS_COUNT, JsonPrimitive(3)),
GoalData(GoalDataKey.TRANSACTION_ID, JsonPrimitive("tx-42")),
),
)LogLevel
LogLevelpublic enum class LogLevel { ERROR, WARN, INFO, DEBUG, TRACE, SILENT }Passed to Builder.logLevel(...) as an inclusive floor — INFO emits error/warn/info and drops debug; SILENT drops everything. The default is DEBUG, so set an explicit level for production builds. See Configuration Options.
PreviewParam
PreviewParamThe one preview type your app calls directly. It parses the value of a convert_preview deep-link query parameter into the (experienceId, variationId) pair that ConvertContext.setPreview(...) takes:
import com.convert.sdk.core.preview.PreviewParam
val parsed: Pair<String, String>? = PreviewParam.parse("12345.67890")
// parsed?.first == "12345" (experienceId)
// parsed?.second == "67890" (variationId)Both segments must be non-empty and numeric-only, split on a single dot. Anything else — a missing dot, an extra dot, an empty or non-numeric segment — returns null. It is a pure function: it never throws, logs, or performs I/O, so guarding on null and falling through to normal behavior is the whole error path. parse is @JvmStatic, so Java callers reach it as PreviewParam.parse(raw).
The forced decision itself is produced internally by PreviewDecision, which returns the same Variation shape as a normal decision with bucketingAllocation = null (no hash was computed). You do not call it — runExperience consults it for you once a preview is set. See the shared QA & Preview guide and Code Examples.
SystemEvents
SystemEventsWell-known event names you can subscribe to with sdk.on(event, callback). Each is a String constant on com.convert.sdk.core.event.SystemEvents:
| Constant | Event name |
|---|---|
READY | ready |
CONFIG_UPDATED | config.updated |
API_QUEUE_RELEASED | api.queue.released |
BUCKETING | bucketing |
CONVERSION | conversion |
SEGMENTS | segments |
LOCATION_ACTIVATED | location.activated |
LOCATION_DEACTIVATED | location.deactivated |
AUDIENCES | audiences |
DATA_STORE_QUEUE_RELEASED | datastore.queue.released |
The shared Event System doc explains when each fires.
EventCallback and SubscriptionToken
EventCallback and SubscriptionTokenpublic fun interface EventCallback {
public fun onEvent(data: Map<String, Any?>)
}EventCallback is a Kotlin fun interface, so Kotlin and Java lambdas both convert to it. sdk.on(event, callback) returns an opaque SubscriptionToken; pass it to sdk.off(event, token) to unsubscribe. See Initialization and Java Interop.
Next steps
- Code Examples — these types in complete flows
- Feature Flags — the shared feature-flag concept doc
Updated 28 days ago