@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
946 lines (936 loc) • 161 kB
JSON
{
"optimizely_sdk_reference": {
"version": "2.0.0",
"generated": "2025-07-20",
"description": "Comprehensive JSON reference for all Optimizely SDKs and APIs with full details for MCP server implementation",
"shared_concepts": {
"event_batching": {
"description": "Batches impression and conversion events to reduce network traffic and improve performance",
"configuration": {
"batch_size": {
"type": "integer",
"default": 10,
"min": 1,
"max": 100,
"description": "Number of events to accumulate before sending"
},
"flush_interval": {
"type": "integer",
"defaults": {
"browser": 1000,
"mobile": 30000,
"server": 30000
},
"unit": "milliseconds",
"min": 100,
"description": "Maximum time to wait before sending batched events"
},
"max_queue_size": {
"type": "integer",
"default": 10000,
"description": "Maximum events to queue before dropping oldest"
}
},
"implementation_examples": {
"javascript": {
"code": "const eventProcessor = createBatchEventProcessor({\n batchSize: 10,\n flushInterval: 2000,\n maxQueueSize: 10000\n});",
"notes": "Events automatically dispatched when batch size reached or interval expires"
},
"java": {
"code": "BatchEventProcessor eventProcessor = BatchEventProcessor.builder()\n .withBatchSize(10)\n .withFlushInterval(30000)\n .withEventDispatcher(customDispatcher)\n .build();",
"notes": "Thread-safe implementation with configurable executor service"
}
}
},
"event_dispatcher": {
"description": "Handles HTTP requests to send impression and conversion events to Optimizely",
"interface": {
"dispatchEvent": {
"params": {
"logEvent": {
"type": "LogEvent",
"fields": {
"url": "string - Optimizely endpoint URL",
"params": "Map<String, ?> - Event payload",
"httpVerb": "string - HTTP method (POST)"
}
}
},
"returns": "void",
"async": true
}
},
"configuration": {
"retry_config": {
"max_retries": 3,
"backoff_multiplier": 2,
"initial_delay": 1000,
"max_delay": 60000
},
"timeout_config": {
"connection_timeout": 10000,
"read_timeout": 60000
},
"concurrency": {
"worker_threads": 2,
"queue_capacity": 1000
}
},
"custom_implementation": {
"example": "class CustomEventDispatcher implements EventDispatcher {\n void dispatchEvent(LogEvent logEvent) {\n // Custom networking logic\n // Custom retry logic\n // Custom logging\n }\n}"
}
},
"user_profile_service": {
"description": "Persists user bucketing decisions to ensure consistent experiences across sessions",
"interface": {
"lookup": {
"signature": "UserProfile lookup(String userId)",
"params": {
"userId": "Unique identifier for the user"
},
"returns": {
"user_id": "string",
"experiment_bucket_map": {
"[experiment_id]": {
"variation_id": "string"
}
}
}
},
"save": {
"signature": "void save(UserProfile userProfile)",
"params": {
"userProfile": {
"user_id": "string",
"experiment_bucket_map": "Map<String, Decision>"
}
}
}
},
"implementation_patterns": {
"in_memory": {
"description": "Simple Map-based storage for testing",
"example": "Map<String, UserProfile> storage = new ConcurrentHashMap<>();"
},
"redis": {
"description": "Distributed caching for production",
"example": "redisClient.set(`optimizely:profile:${userId}`, JSON.stringify(profile))"
},
"database": {
"description": "Persistent storage for long-term consistency",
"example": "INSERT INTO user_profiles (user_id, profile_data) VALUES (?, ?)"
}
}
},
"bucketing_algorithm": {
"description": "MurmurHash3-based deterministic bucketing for consistent variation assignment",
"process": [
{
"step": "Generate bucketing key",
"formula": "bucketingKey = `${userId}${experimentId}`"
},
{
"step": "Compute hash",
"formula": "hashValue = murmur3_128(bucketingKey)"
},
{
"step": "Normalize to bucket",
"formula": "bucket = (hashValue / MAX_HASH_VALUE) * 10000"
},
{
"step": "Find variation",
"logic": "Iterate through traffic allocation ranges to find matching variation"
}
],
"traffic_allocation": {
"example": [
{ "entityId": "variation_1", "endOfRange": 5000 },
{ "entityId": "variation_2", "endOfRange": 10000 }
],
"note": "Each variation gets percentage of 0-10000 range"
}
},
"notification_listeners": {
"types": {
"DECISION": {
"description": "Fired whenever a flag decision is made",
"payload": {
"type": "flag | ab-test | feature-test",
"userId": "string",
"attributes": "object",
"decisionInfo": {
"flagKey": "string",
"enabled": "boolean",
"variationKey": "string",
"ruleKey": "string",
"reasons": "string[]",
"decisionEventDispatched": "boolean"
}
}
},
"TRACK": {
"description": "Fired when track event is called",
"payload": {
"eventKey": "string",
"userId": "string",
"attributes": "object",
"eventTags": "object",
"logEvent": "LogEvent object"
}
},
"LOG_EVENT": {
"description": "Fired before event dispatch to logging endpoint",
"payload": {
"url": "string",
"params": "object",
"httpVerb": "string"
}
},
"CONFIG_UPDATE": {
"description": "Fired when datafile is updated",
"payload": {
"revision": "string",
"datafileStr": "string"
}
}
},
"registration_example": {
"code": "notificationCenter.addNotificationListener(\n NotificationType.DECISION,\n notification -> {\n Map<String, ?> decisionInfo = notification.getDecisionInfo();\n logger.info(\"Decision made for flag: {}\", decisionInfo.get(\"flagKey\"));\n }\n);"
}
},
"decide_options": {
"DISABLE_DECISION_EVENT": {
"description": "Prevents sending impression event for this decision",
"use_case": "Reducing event volume for high-traffic features"
},
"ENABLED_FLAGS_ONLY": {
"description": "Only returns flags that evaluated to enabled",
"use_case": "Simplifying client-side feature checks"
},
"IGNORE_USER_PROFILE_SERVICE": {
"description": "Makes fresh bucketing decision ignoring saved profile",
"use_case": "A/B test re-bucketing or testing scenarios"
},
"INCLUDE_REASONS": {
"description": "Includes array of decision reasons in response",
"use_case": "Debugging and decision transparency"
},
"EXCLUDE_VARIABLES": {
"description": "Omits variable values from decision response",
"use_case": "Reducing payload size when variables not needed"
}
},
"datafile_schema": {
"description": "JSON configuration that powers all SDK decisions",
"structure": {
"version": "string - Datafile version (4)",
"rollouts": "array - Feature rollouts configuration",
"typedAudiences": "array - Audience definitions",
"experiments": "array - A/B test configurations",
"featureFlags": "array - Feature flag definitions",
"events": "array - Conversion event definitions",
"audiences": "array - Legacy audience format",
"groups": "array - Experiment groups",
"attributes": "array - User attribute definitions",
"accountId": "string - Optimizely account ID",
"projectId": "string - Project identifier",
"variables": "array - Feature variable definitions",
"revision": "string - Datafile revision number"
},
"accessing_config": {
"get_all_feature_flags": {
"description": "Returns array of all feature flag keys",
"signature": "String[] getAllFeatureKeys()",
"example_return": "[\"dark_mode\", \"new_checkout\", \"search_v2\"]"
},
"get_feature_flag": {
"description": "Returns complete feature flag configuration",
"signature": "FeatureFlag getFeatureFlag(String flagKey)",
"return_structure": {
"id": "string",
"key": "string",
"experimentIds": "string[]",
"rolloutId": "string",
"variables": [{
"id": "string",
"key": "string",
"type": "boolean | integer | double | string",
"defaultValue": "string"
}]
}
},
"get_optimizely_config": {
"description": "Returns simplified config for client-side usage",
"signature": "OptimizelyConfig getOptimizelyConfig()",
"return_structure": {
"experimentsMap": {
"[experimentKey]": {
"id": "string",
"key": "string",
"variationsMap": {
"[variationKey]": {
"id": "string",
"key": "string",
"variablesMap": {
"[variableKey]": {
"id": "string",
"key": "string",
"type": "string",
"value": "string"
}
}
}
}
}
},
"featuresMap": {
"[featureKey]": {
"id": "string",
"key": "string",
"experimentRules": [],
"deliveryRules": [],
"variablesMap": {}
}
}
}
}
}
}
},
"feature_experimentation_sdks": {
"android": {
"package": "com.optimizely.ab:android-sdk",
"current_version": "4.0.0+",
"min_api_level": 21,
"installation": {
"gradle": {
"dependencies": "implementation 'com.optimizely.ab:android-sdk:4.0.0'",
"repository": "mavenCentral()"
}
},
"initialization": {
"manager_builder": {
"class": "OptimizelyManager.Builder",
"complete_example": "val optimizelyManager = OptimizelyManager.builder()\n .withSDKKey(\"YOUR_SDK_KEY\")\n .withDatafileDownloadInterval(15, TimeUnit.MINUTES)\n .withEventDispatchInterval(60, TimeUnit.SECONDS)\n .withTimeoutForODPEventDispatch(10)\n .withDefaultDecideOptions(listOf(\n OptimizelyDecideOption.INCLUDE_REASONS,\n OptimizelyDecideOption.DISABLE_DECISION_EVENT\n ))\n .withVuidEnabled()\n .withODPManager(odpManager)\n .build(applicationContext)",
"methods": {
"withSDKKey": {
"signature": "fun withSDKKey(sdkKey: String): Builder",
"required": true,
"description": "Sets the SDK key for your Optimizely project"
},
"withDatafileDownloadInterval": {
"signature": "fun withDatafileDownloadInterval(interval: Long, timeUnit: TimeUnit): Builder",
"default": "15 minutes",
"min": "15 minutes (Android constraint)",
"description": "How often to poll for datafile updates"
},
"withEventDispatchInterval": {
"signature": "fun withEventDispatchInterval(interval: Long, timeUnit: TimeUnit): Builder",
"default": "60 seconds",
"min": "1 second",
"description": "How often to send batched events"
},
"withEventHandler": {
"signature": "fun withEventHandler(handler: EventHandler): Builder",
"description": "Custom event batching/dispatch handler"
},
"withErrorHandler": {
"signature": "fun withErrorHandler(handler: ErrorHandler): Builder",
"description": "Custom error handling implementation"
},
"withUserProfileService": {
"signature": "fun withUserProfileService(service: UserProfileService): Builder",
"description": "Custom user profile persistence"
},
"withTimeoutForODPEventDispatch": {
"signature": "fun withTimeoutForODPEventDispatch(timeout: Int): Builder",
"default": "10 seconds",
"description": "Timeout for ODP event dispatch"
},
"withSegmentsCacheSize": {
"signature": "fun withSegmentsCacheSize(size: Int): Builder",
"default": 100,
"description": "Number of segments to cache"
},
"withSegmentsCacheTimeoutInSecs": {
"signature": "fun withSegmentsCacheTimeoutInSecs(timeout: Int): Builder",
"default": 600,
"description": "Segment cache expiration time"
},
"withTimeoutForSegmentFetchInSecs": {
"signature": "fun withTimeoutForSegmentFetchInSecs(timeout: Int): Builder",
"default": 10,
"description": "Timeout for fetching segments"
},
"withODPManager": {
"signature": "fun withODPManager(odpManager: ODPManager): Builder",
"description": "Custom ODP manager for real-time segments"
},
"withVuidEnabled": {
"signature": "fun withVuidEnabled(): Builder",
"description": "Enable automatic visitor ID generation",
"since": "v5.0.0"
},
"withDefaultDecideOptions": {
"signature": "fun withDefaultDecideOptions(options: List<OptimizelyDecideOption>): Builder",
"description": "Default options for all decide calls"
}
}
},
"initialization_patterns": {
"synchronous": {
"description": "Blocks until client is ready (uses cached datafile)",
"example": "// Synchronous - prioritizes speed\nval optimizelyClient = optimizelyManager.initialize(context, datafile)\n\n// Use immediately\nval user = optimizelyClient.createUserContext(\"user123\")\nval decision = user.decide(\"feature_flag_key\")"
},
"asynchronous": {
"description": "Downloads fresh datafile before initializing",
"example": "// Asynchronous - prioritizes accuracy\noptimizelyManager.initialize(context, null) { client ->\n client?.let {\n val user = it.createUserContext(\"user123\")\n val decision = user.decide(\"feature_flag_key\")\n }\n}"
},
"coroutines": {
"description": "Kotlin coroutines pattern",
"example": "lifecycleScope.launch {\n val client = withContext(Dispatchers.IO) {\n optimizelyManager.initialize(context, null)\n }\n val user = client.createUserContext(\"user123\")\n val decision = user.decide(\"feature_flag_key\")\n}"
}
}
},
"api": {
"createUserContext": {
"signature": "fun createUserContext(userId: String?, attributes: Map<String, Any>? = null): OptimizelyUserContext",
"params": {
"userId": {
"type": "String?",
"description": "Unique identifier for the user, null for anonymous"
},
"attributes": {
"type": "Map<String, Any>?",
"description": "Custom attributes for targeting",
"supported_types": ["String", "Boolean", "Int", "Long", "Float", "Double"],
"reserved_attributes": ["$opt_bucketing_id"]
}
},
"returns": "OptimizelyUserContext instance",
"example": "val userContext = optimizelyClient.createUserContext(\n userId = \"user_123\",\n attributes = mapOf(\n \"plan_type\" to \"premium\",\n \"account_age_days\" to 365,\n \"is_employee\" to false,\n \"location\" to \"US\",\n \"device_type\" to \"mobile\"\n )\n)"
},
"decide": {
"signature": "fun decide(key: String, options: List<OptimizelyDecideOption> = emptyList()): OptimizelyDecision",
"params": {
"key": "Feature flag key",
"options": "List of decide options to override defaults"
},
"returns": {
"class": "OptimizelyDecision",
"properties": {
"variationKey": "String? - Variation identifier",
"enabled": "Boolean - Whether feature is enabled",
"variables": "OptimizelyJSON - Feature variables",
"ruleKey": "String? - Experiment or rollout rule key",
"flagKey": "String - Feature flag key",
"userContext": "OptimizelyUserContext - User context used",
"reasons": "List<String> - Decision reasons (if requested)"
}
},
"complete_example": "val decision = userContext.decide(\n \"checkout_flow\",\n listOf(OptimizelyDecideOption.INCLUDE_REASONS)\n)\n\nif (decision.enabled) {\n val buttonColor = decision.variables.getValue(\"button_color\", String::class.java)\n val checkoutSteps = decision.variables.getValue(\"checkout_steps\", Int::class.java)\n \n // Use the feature variables\n configureCheckout(buttonColor, checkoutSteps)\n \n // Log decision reasons\n decision.reasons?.forEach { reason ->\n logger.debug(\"Decision reason: $reason\")\n }\n}"
},
"decideAll": {
"signature": "fun decideAll(options: List<OptimizelyDecideOption> = emptyList()): Map<String, OptimizelyDecision>",
"description": "Evaluates all feature flags for the user",
"returns": "Map of flag keys to decisions",
"example": "val allDecisions = userContext.decideAll(\n listOf(OptimizelyDecideOption.ENABLED_FLAGS_ONLY)\n)\n\nallDecisions.forEach { (flagKey, decision) ->\n if (decision.enabled) {\n logger.info(\"Feature $flagKey is enabled with variation ${decision.variationKey}\")\n }\n}"
},
"decideForKeys": {
"signature": "fun decideForKeys(keys: List<String>, options: List<OptimizelyDecideOption> = emptyList()): Map<String, OptimizelyDecision>",
"description": "Evaluates specified feature flags",
"example": "val decisions = userContext.decideForKeys(\n listOf(\"feature_1\", \"feature_2\", \"feature_3\"),\n listOf(OptimizelyDecideOption.EXCLUDE_VARIABLES)\n)"
},
"trackEvent": {
"signature": "fun trackEvent(eventKey: String, eventTags: Map<String, Any>? = null)",
"params": {
"eventKey": "Event identifier from datafile",
"eventTags": {
"type": "Map<String, Any>?",
"reserved_tags": {
"revenue": "Integer value in cents",
"value": "Float metric value"
}
}
},
"example": "// Track conversion with revenue\nuserContext.trackEvent(\n eventKey = \"purchase_complete\",\n eventTags = mapOf(\n \"revenue\" to 4999, // $49.99 in cents\n \"payment_method\" to \"credit_card\",\n \"items_count\" to 3\n )\n)\n\n// Track custom metric\nuserContext.trackEvent(\n eventKey = \"video_watched\",\n eventTags = mapOf(\n \"value\" to 0.75, // 75% completion\n \"video_id\" to \"abc123\"\n )\n)"
},
"setAttribute": {
"signature": "fun setAttribute(key: String, value: Any)",
"description": "Updates user attribute after context creation",
"supported_types": ["String", "Boolean", "Int", "Long", "Float", "Double"],
"example": "userContext.setAttribute(\"vip_status\", true)\nuserContext.setAttribute(\"lifetime_value\", 15000)"
},
"getOptimizelyConfig": {
"signature": "fun getOptimizelyConfig(): OptimizelyConfig?",
"description": "Returns datafile configuration in simplified format",
"usage": "val config = optimizelyClient.getOptimizelyConfig()\nval experiments = config?.experimentsMap\nval features = config?.featuresMap"
},
"close": {
"signature": "fun close()",
"description": "Cleanup resources and flush events",
"usage": "override fun onDestroy() {\n optimizelyClient.close()\n super.onDestroy()\n}"
}
},
"android_specific": {
"permissions": {
"required": ["android.permission.INTERNET"],
"optional": ["android.permission.ACCESS_NETWORK_STATE"]
},
"proguard": {
"rules": "-keep class com.optimizely.** { *; }\n-keep class org.slf4j.** { *; }"
},
"lifecycle_integration": {
"application_class": "class MyApp : Application() {\n override fun onCreate() {\n super.onCreate()\n // Initialize Optimizely\n }\n}",
"activity_usage": "class MainActivity : AppCompatActivity() {\n private lateinit var optimizelyClient: OptimizelyClient\n \n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n optimizelyClient = (application as MyApp).optimizelyClient\n }\n}"
}
}
},
"swift": {
"package": "Optimizely-Swift-SDK",
"current_version": "4.0+",
"min_ios_version": "10.0",
"installation": {
"cocoapods": {
"podfile": "pod 'Optimizely-Swift-SDK', '~> 4.0'",
"subspecs": [
"Core - Main SDK functionality",
"DatafileManagerService - Datafile management",
"UserProfileService - User profile persistence"
]
},
"swift_package_manager": {
"package": ".package(url: \"https://github.com/optimizely/swift-sdk.git\", .upToNextMinor(from: \"4.0.0\"))",
"targets": ["Optimizely"]
},
"carthage": {
"cartfile": "github \"optimizely/swift-sdk\" ~> 4.0"
}
},
"initialization": {
"client_creation": {
"basic": {
"example": "let optimizely = OptimizelyClient(sdkKey: \"YOUR_SDK_KEY\")"
},
"advanced": {
"example": "let optimizely = OptimizelyClient(\n sdkKey: \"YOUR_SDK_KEY\",\n logger: CustomLogger(),\n eventDispatcher: CustomEventDispatcher(),\n userProfileService: CustomUserProfileService(),\n periodicDownloadInterval: 120, // seconds\n defaultLogLevel: .debug,\n defaultDecideOptions: [.includeReasons],\n settings: OptimizelySdkSettings(\n segmentsCacheSize: 100,\n segmentsCacheTimeoutInSecs: 600,\n timeoutForSegmentFetchInSecs: 10,\n timeoutForOdpEventInSecs: 10,\n disableOdp: false,\n enableVuid: true\n )\n)",
"parameters": {
"sdkKey": {
"type": "String",
"required": true,
"description": "SDK key from Optimizely app"
},
"logger": {
"type": "OPTLogger?",
"description": "Custom logging implementation"
},
"eventDispatcher": {
"type": "OPTEventDispatcher?",
"description": "Custom event dispatch"
},
"userProfileService": {
"type": "OPTUserProfileService?",
"description": "User profile persistence"
},
"periodicDownloadInterval": {
"type": "Int?",
"unit": "seconds",
"default": 600,
"min": 120,
"description": "Datafile polling interval"
},
"defaultLogLevel": {
"type": "OptimizelyLogLevel?",
"options": [".error", ".warning", ".info", ".debug"],
"default": ".info"
},
"defaultDecideOptions": {
"type": "[OptimizelyDecideOption]?",
"description": "Default options for decide calls"
},
"settings": {
"type": "OptimizelySdkSettings?",
"description": "Advanced SDK settings"
}
}
}
},
"startup_patterns": {
"synchronous": {
"description": "Blocks until ready with datafile",
"example": "do {\n try optimizely.start(datafile: cachedDatafile)\n // SDK is ready immediately\n let user = optimizely.createUserContext(userId: \"user123\")\n} catch {\n print(\"Failed to start: \\(error)\")\n}"
},
"asynchronous_callback": {
"description": "Downloads fresh datafile",
"example": "optimizely.start { result in\n switch result {\n case .success(let data):\n // SDK is ready\n let user = optimizely.createUserContext(userId: \"user123\")\n case .failure(let error):\n print(\"Failed to start: \\(error)\")\n }\n}"
},
"async_await": {
"description": "Modern async/await pattern (iOS 13+)",
"example": "Task {\n do {\n let data = try await optimizely.start()\n let user = optimizely.createUserContext(userId: \"user123\")\n } catch {\n print(\"Failed to start: \\(error)\")\n }\n}"
}
}
},
"api": {
"createUserContext": {
"signature": "func createUserContext(userId: String? = nil, attributes: [String: Any]? = nil) -> OptimizelyUserContext",
"params": {
"userId": {
"type": "String?",
"description": "Unique user identifier, nil for anonymous"
},
"attributes": {
"type": "[String: Any]?",
"supported_types": ["String", "Bool", "Int", "Double"],
"reserved": ["$opt_bucketing_id"]
}
},
"example": "let user = optimizely.createUserContext(\n userId: \"user_123\",\n attributes: [\n \"plan_type\": \"premium\",\n \"device_model\": \"iPhone14,2\",\n \"app_version\": \"2.1.0\",\n \"is_beta_user\": true,\n \"sessions_count\": 42\n ]\n)"
},
"decide": {
"signature": "func decide(key: String, options: [OptimizelyDecideOption] = []) -> OptimizelyDecision",
"returns": {
"properties": {
"variationKey": "String? - Selected variation",
"enabled": "Bool - Feature enabled state",
"variables": "OptimizelyJSON - Variable values",
"ruleKey": "String? - Matching rule",
"flagKey": "String - Feature flag key",
"userContext": "OptimizelyUserContext",
"reasons": "[String]? - Decision reasons"
}
},
"variable_access": {
"methods": {
"getValue": "func getValue<T>(jsonPath: String?, type: T.Type) -> T?",
"getString": "func getString(jsonPath: String?) -> String?",
"getInt": "func getInt(jsonPath: String?) -> Int?",
"getDouble": "func getDouble(jsonPath: String?) -> Double?",
"getBool": "func getBool(jsonPath: String?) -> Bool?"
},
"example": "let decision = user.decide(key: \"new_feature\")\n\nif decision.enabled {\n let color = decision.variables.getString(jsonPath: \"button_color\") ?? \"blue\"\n let maxItems = decision.variables.getInt(jsonPath: \"max_items\") ?? 10\n let price = decision.variables.getDouble(jsonPath: \"base_price\") ?? 9.99\n let showBanner = decision.variables.getBool(jsonPath: \"show_banner\") ?? false\n \n // Access nested values\n let headerText = decision.variables.getString(jsonPath: \"ui.header.text\")\n}"
}
},
"decideAll": {
"signature": "func decideAll(options: [OptimizelyDecideOption] = []) -> [String: OptimizelyDecision]",
"example": "let allDecisions = user.decideAll(options: [.enabledFlagsOnly])\n\nfor (flagKey, decision) in allDecisions {\n print(\"Flag: \\(flagKey), Enabled: \\(decision.enabled)\")\n}"
},
"trackEvent": {
"signature": "func trackEvent(eventKey: String, eventTags: [String: Any]? = nil)",
"reserved_tags": {
"revenue": {
"type": "Int",
"unit": "cents",
"description": "Revenue value for the event"
},
"value": {
"type": "Double",
"description": "Numeric metric value"
}
},
"example": "// Revenue tracking\nuser.trackEvent(\n eventKey: \"purchase_complete\",\n eventTags: [\n \"revenue\": 2999, // $29.99\n \"currency\": \"USD\",\n \"payment_method\": \"apple_pay\"\n ]\n)\n\n// Custom metrics\nuser.trackEvent(\n eventKey: \"level_completed\",\n eventTags: [\n \"value\": Double(levelScore),\n \"level\": currentLevel,\n \"time_spent\": timeInSeconds\n ]\n)"
},
"setAttribute": {
"signature": "func setAttribute(key: String, value: Any)",
"thread_safety": "Thread-safe",
"example": "user.setAttribute(key: \"subscription_renewed\", value: true)\nuser.setAttribute(key: \"total_purchases\", value: 25)"
},
"getOptimizelyConfig": {
"signature": "func getOptimizelyConfig() -> OptimizelyConfig?",
"returns": {
"experimentsMap": "[String: OptimizelyExperiment]",
"featuresMap": "[String: OptimizelyFeature]",
"revision": "String"
}
},
"close": {
"signature": "func close()",
"description": "Flushes events and releases resources",
"usage": "deinit {\n optimizely.close()\n}"
}
},
"swift_specific": {
"notification_center": {
"example": "let notificationId = optimizely.notificationCenter.addNotificationListener(\n notificationType: .decision,\n notificationListener: { notification in\n guard let decision = notification.decision else { return }\n Analytics.track(\"Feature Evaluated\", properties: [\n \"flag_key\": decision.flagKey,\n \"variation\": decision.variationKey ?? \"none\"\n ])\n }\n)"
},
"error_handling": {
"types": [
"OptimizelyError.sdkNotReady",
"OptimizelyError.invalidDatafile",
"OptimizelyError.invalidArgument"
]
},
"objective_c_compatibility": {
"bridging": "Full @objc annotations for Objective-C projects"
}
}
},
"javascript_sdk_v6": {
"package": "@optimizely/optimizely-sdk",
"version": "6.0.0+",
"platforms": ["Browser", "Node.js", "Edge Workers"],
"installation": {
"npm": "npm install @optimizely/optimizely-sdk",
"yarn": "yarn add @optimizely/optimizely-sdk",
"cdn": "<script src=\"https://unpkg.com/@optimizely/optimizely-sdk@6.0/dist/optimizely.browser.umd.min.js\"></script>"
},
"initialization": {
"factory_functions": {
"createInstance": {
"signature": "function createInstance(config: Config): OptimizelyClient",
"config_options": {
"datafile": {
"type": "object | string",
"description": "Initial datafile (optional if using sdkKey)"
},
"projectConfigManager": {
"type": "ProjectConfigManager",
"description": "Manages datafile fetching and caching"
},
"eventProcessor": {
"type": "EventProcessor",
"description": "Handles event batching and dispatch"
},
"logger": {
"type": "Logger",
"default": "NoOpLogger",
"levels": ["ERROR", "WARNING", "INFO", "DEBUG"]
},
"errorHandler": {
"type": "ErrorHandler",
"description": "Custom error handling"
},
"defaultDecideOptions": {
"type": "OptimizelyDecideOption[]",
"description": "Default options for all decide calls"
},
"odpManager": {
"type": "OdpManager",
"description": "Real-time segments manager"
},
"vuidManager": {
"type": "VuidManager",
"description": "Visitor ID management"
},
"clientEngine": {
"type": "string",
"description": "Identifier for SDK platform"
}
}
}
},
"project_config_managers": {
"polling_manager": {
"function": "createPollingProjectConfigManager",
"signature": "function createPollingProjectConfigManager(options: PollingOptions): ProjectConfigManager",
"options": {
"sdkKey": {
"type": "string",
"required": true
},
"datafile": {
"type": "object | string",
"description": "Initial cached datafile"
},
"autoUpdate": {
"type": "boolean",
"default": true,
"description": "Enable automatic polling"
},
"updateInterval": {
"type": "number",
"default": 60000,
"unit": "milliseconds",
"min": 30000
},
"urlTemplate": {
"type": "string",
"default": "https://cdn.optimizely.com/datafiles/%s.json",
"description": "URL pattern for datafile"
}
},
"example": "const configManager = createPollingProjectConfigManager({\n sdkKey: 'YOUR_SDK_KEY',\n autoUpdate: true,\n updateInterval: 5 * 60 * 1000, // 5 minutes\n datafile: getCachedDatafile() // Optional cached datafile\n});"
},
"static_manager": {
"function": "createStaticProjectConfigManager",
"usage": "For environments where datafile doesn't change",
"example": "const configManager = createStaticProjectConfigManager({\n datafile: datafileJSON\n});"
}
},
"event_processors": {
"batch_processor": {
"function": "createBatchEventProcessor",
"signature": "function createBatchEventProcessor(options: BatchEventProcessorOptions): EventProcessor",
"options": {
"batchSize": {
"type": "number",
"default": 10,
"description": "Events per batch"
},
"flushInterval": {
"type": "number",
"default": 2000,
"unit": "milliseconds"
},
"maxQueueSize": {
"type": "number",
"default": 10000,
"description": "Max queued events"
},
"eventDispatcher": {
"type": "EventDispatcher",
"description": "Custom network layer"
}
},
"example": "const eventProcessor = createBatchEventProcessor({\n batchSize: 20,\n flushInterval: 5000,\n maxQueueSize: 20000,\n eventDispatcher: customEventDispatcher\n});"
}
},
"complete_initialization": {
"browser": "// Browser with automatic updates\nimport { createInstance, createPollingProjectConfigManager, createBatchEventProcessor } from '@optimizely/optimizely-sdk';\n\nconst optimizely = createInstance({\n projectConfigManager: createPollingProjectConfigManager({\n sdkKey: 'YOUR_SDK_KEY',\n autoUpdate: true,\n updateInterval: 5 * 60 * 1000\n }),\n eventProcessor: createBatchEventProcessor({\n batchSize: 10,\n flushInterval: 2000\n }),\n defaultDecideOptions: [\n 'INCLUDE_REASONS',\n 'DISABLE_DECISION_EVENT'\n ],\n logger: window.console\n});\n\n// Wait for initialization\noptimizely.onReady().then(() => {\n console.log('Optimizely is ready');\n});",
"node": "// Node.js with custom components\nimport { createInstance, createPollingProjectConfigManager, createBatchEventProcessor, createLogger } from '@optimizely/optimizely-sdk';\n\nconst optimizely = createInstance({\n projectConfigManager: createPollingProjectConfigManager({\n sdkKey: process.env.OPTIMIZELY_SDK_KEY,\n autoUpdate: true,\n updateInterval: 60000\n }),\n eventProcessor: createBatchEventProcessor({\n batchSize: 100,\n flushInterval: 30000,\n maxQueueSize: 100000\n }),\n logger: createLogger({\n logLevel: 'INFO',\n logToConsole: true\n }),\n defaultDecideOptions: ['EXCLUDE_VARIABLES']\n});\n\n// Async initialization\nasync function initialize() {\n await optimizely.onReady({ timeout: 5000 });\n return optimizely;\n}"
}
},
"api": {
"createUserContext": {
"signature": "createUserContext(userId?: string | null, attributes?: UserAttributes): OptimizelyUserContext",
"params": {
"userId": {
"type": "string | null",
"description": "User identifier or null for anonymous"
},
"attributes": {
"type": "object",
"supported_types": ["string", "number", "boolean"],
"reserved": ["$opt_bucketing_id"]
}
},
"returns": {
"class": "OptimizelyUserContext",
"methods": [
"decide",
"decideAsync",
"decideAll",
"decideAllAsync",
"decideForKeys",
"decideForKeysAsync",
"trackEvent",
"trackEventAsync",
"setAttribute",
"fetchQualifiedSegments",
"isQualifiedFor"
]
},
"example": "const user = optimizely.createUserContext('user_123', {\n plan_type: 'enterprise',\n company_size: 500,\n industry: 'technology',\n beta_tester: true,\n account_age_days: 180\n});\n\n// Update attributes after creation\nuser.setAttribute('last_purchase_date', '2024-03-15');\nuser.setAttribute('lifetime_value', 5000);"
},
"decide": {
"signature": "decide(key: string, options?: OptimizelyDecideOption[]): OptimizelyDecision",
"async_version": "decideAsync(key: string, options?: OptimizelyDecideOption[]): Promise<OptimizelyDecision>",
"returns": {
"properties": {
"variationKey": "string | null",
"enabled": "boolean",
"variables": "{ [key: string]: any }",
"ruleKey": "string | null",
"flagKey": "string",
"userContext": "OptimizelyUserContext",
"reasons": "string[] | undefined"
}
},
"options": {
"DISABLE_DECISION_EVENT": "Skip impression event",
"ENABLED_FLAGS_ONLY": "Only return enabled flags",
"IGNORE_USER_PROFILE_SERVICE": "Fresh bucketing",
"INCLUDE_REASONS": "Include decision path",
"EXCLUDE_VARIABLES": "Omit variable values"
},
"complete_example": "// Synchronous decision\nconst decision = user.decide('checkout_flow', [\n 'INCLUDE_REASONS'\n]);\n\nif (decision.enabled) {\n const { \n button_text,\n button_color,\n steps_count,\n show_testimonials \n } = decision.variables;\n \n console.log(`Using ${decision.variationKey} variation`);\n console.log('Decision reasons:', decision.reasons);\n \n // Apply feature configuration\n updateCheckoutUI({\n buttonText: button_text || 'Checkout',\n buttonColor: button_color || '#0066cc',\n steps: steps_count || 3,\n testimonials: show_testimonials || false\n });\n}\n\n// Async decision with ODP\nconst asyncDecision = await user.decideAsync('personalized_homepage', [\n 'INCLUDE_REASONS'\n]);\n\n// This will fetch ODP segments before deciding\nif (asyncDecision.enabled) {\n renderPersonalizedContent(asyncDecision.variables);\n}"
},
"decideAll": {
"signature": "decideAll(options?: OptimizelyDecideOption[]): { [key: string]: OptimizelyDecision }",
"async_version": "decideAllAsync(options?: OptimizelyDecideOption[]): Promise<{ [key: string]: OptimizelyDecision }>",
"example": "// Get all flag decisions\nconst allDecisions = user.decideAll(['ENABLED_FLAGS_ONLY']);\n\nObject.entries(allDecisions).forEach(([flagKey, decision]) => {\n console.log(`${flagKey}: ${decision.enabled ? 'enabled' : 'disabled'}`);\n if (decision.enabled) {\n applyFeature(flagKey, decision.variables);\n }\n});\n\n// Async version fetches ODP segments first\nconst asyncDecisions = await user.decideAllAsync();"
},
"decideForKeys": {
"signature": "decideForKeys(keys: string[], options?: OptimizelyDecideOption[]): { [key: string]: OptimizelyDecision }",
"async_version": "decideForKeysAsync(keys: string[], options?: OptimizelyDecideOption[]): Promise<{ [key: string]: OptimizelyDecision }>",
"example": "// Evaluate specific features\nconst criticalFeatures = user.decideForKeys([\n 'payment_gateway',\n 'shipping_calculator', \n 'recommendation_engine'\n], ['EXCLUDE_VARIABLES']);\n\n// Check which are enabled\nconst enabledFeatures = Object.entries(criticalFeatures)\n .filter(([_, decision]) => decision.enabled)\n .map(([key, _]) => key);"
},
"trackEvent": {
"signature": "trackEvent(eventKey: string, eventTags?: EventTags): void",
"async_version": "trackEventAsync(eventKey: string, eventTags?: EventTags): Promise<void>",
"event_tags": {
"revenue": {
"type": "number",
"unit": "cents",
"description": "Revenue in cents"
},
"value": {
"type": "number",
"description": "Numeric metric"
},
"custom": {
"type": "string | number | boolean",
"description": "Any custom tags"
}
},
"examples": {
"revenue": "// Track purchase with revenue\nuser.trackEvent('purchase_complete', {\n revenue: 4999, // $49.99\n currency: 'USD',\n payment_method: 'stripe',\n items_count: 3,\n shipping_method: 'express'\n});",
"engagement": "// Track engagement metric\nuser.trackEvent('video_engagement', {\n value: 0.85, // 85% watched\n video_id: 'abc123',\n video_duration: 300,\n quality: '1080p'\n});",
"custom": "// Track custom event\nuser.trackEvent('feature_interaction', {\n feature_name: 'ai_assistant',\n interaction_type: 'query',\n response_time_ms: 230\n});"
}
},
"setAttribute": {
"signature": "setAttribute(key: string, value: any): void",
"thread_safety": "Thread-safe in Node.js",
"example": "// Update user attributes dynamically\nuser.setAttribute('subscription_status', 'active');\nuser.setAttribute('last_login', new Date().toISOString());\nuser.setAttribute('feature_access_level', 3);\n\n// Attributes affect future decisions\nconst decision = user.decide('premium_feature');"
},
"fetchQualifiedSegments": {
"signature": "fetchQualifiedSegments(options?: string[]): Promise<boolean>",
"description": "Fetches ODP segments for user",
"params": {
"options": "Array of segment fetch options"
},
"example": "// Fetch user segments from ODP\nconst success = await user.fetchQualifiedSegments();\n\nif (success) {\n // Check if user qualifies for segment\n const isVip = user.isQualifiedFor('vip_customers');\n const isHighValue = user.isQualifiedFor('high_value_segment');\n}"
},
"onReady": {
"signature": "onReady(config?: { timeout?: number }): Promise<void>",
"description": "Wait for SDK initialization",
"example": "// Wait with timeout\ntry {\n await optimizely.onReady({ timeout: 5000 });\n console.log('SDK ready');\n} catch (err) {\n console.error('SDK initialization timeout');\n // Use cached datafile or defaults\n}"
},
"close": {
"signature": "close(): void",
"description": "Cleanup and flush events",
"example": "// Graceful shutdown\nprocess.on('SIGTERM', () => {\n optimizely.close();\n process.exit(0);\n});"
},
"getOptimizelyConfig": {
"signature": "getOptimizelyConfig(): OptimizelyConfig",
"returns": {
"revision": "string",
"sdkKey": "string",
"environmentKey": "string",
"experimentsMap": {
"[experimentKey]": {
"id": "string",
"key": "string",
"audiences": "string",
"variationsMap": {
"[variationKey]": {
"id": "string",
"key": "string",
"featureEnabled": "boolean",
"variablesMap": {
"[variableKey]": {
"id": "string",
"key": "string",
"type": "string",
"value": "string | number | boolean"
}
}
}
}
}
},
"featuresMap": {
"[featureKey]": {
"id": "string",
"key": "string",
"experimentRules