UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

946 lines (936 loc) 167 kB
{ "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