Configuration Options

Field-by-field reference for the SDKConfig, TransportConfig, and RefreshConfig frozen dataclasses

Field-by-field reference for every config dataclass the SDK accepts: SDKConfig, TransportConfig, and RefreshConfig. For the narrative on init modes and the refresh lifecycle, see Initialization.

All three are plain frozen dataclasses — no Pydantic dependency. Validation runs in __post_init__ and raises typed errors from the ConvertSDKError hierarchy. Import them from convert_sdk:

from convert_sdk import SDKConfig, TransportConfig, RefreshConfig

Note: There is no TrackingConfig class. Tracking queue and batch settings (batch_size, auto_flush_interval_ms) are fields directly on SDKConfig.


SDKConfig

Top-level config passed to Core(SDKConfig(...)). Exactly one of sdk_key or data must be provided; passing both or neither raises InvalidConfigError.

from convert_sdk import Core, SDKConfig, TransportConfig, RefreshConfig
import logging

core = Core(
    SDKConfig(
        sdk_key="your-sdk-key",        # remote mode — XOR with data=
        # data={"account_id": ...},    # direct/offline mode — XOR with sdk_key=
        environment="production",
        cache_level=None,
        transport=TransportConfig(),
        batch_size=10,
        auto_flush_interval_ms=None,
        data_store=None,
        logger=None,
        refresh=None,
    )
).initialize()

Field reference

FieldTypeDefaultPurpose
sdk_keystr | NoneNoneConvert project SDK key. Used when fetching config remotely over HTTPS. Mutually exclusive with data.
datadict | NoneNoneInline project-config payload for direct/offline initialization. No network call is made. Mutually exclusive with sdk_key.
environmentstr | NoneNoneOptional environment filter. When set, the config route appends environment={environment} (JS SDK parity).
cache_levelstr | NoneNoneOptional cache hint. "low" appends _conv_low_cache=1 to the config route (JS SDK parity). Any other non-None value raises InvalidConfigError.
transportTransportConfigTransportConfig()Network settings for config-fetch and tracking delivery. See TransportConfig below.
batch_sizeint10Number of queued tracking events that triggers an automatic batch-size queue release. Must be a positive integer. Mirrors the JS SDK DEFAULT_BATCH_SIZE.
auto_flush_interval_msint | NoneNoneOpt-in periodic-flush interval in milliseconds. None keeps the lifecycle explicit-flush-only — safe in every runtime. When set, a daemonic threading.Timer periodically releases the queue. Must be a positive integer when provided.
data_storeDataStore | NoneNoneOptional shared persistence adapter implementing the DataStore protocol. None selects the default per-process InMemoryDataStore. Affects deduplication state and visitor-state persistence.
loggerlogging.Logger | NoneNoneOptional caller-supplied logging.Logger. None uses the "convert_sdk" namespace logger (logging.getLogger("convert_sdk")). The SDK never calls logging.basicConfig(), adds handlers, or changes the level — your application owns handler/level configuration.
refreshRefreshConfig | NoneNoneOpt-in background config refresh policy. None (default) keeps MVP behavior: no daemon thread, no refresh events, zero added cost. Only effective in sdk_key (remote) mode.

Validation rules

All validation runs in SDKConfig.__post_init__ and raises InvalidConfigError:

RuleError message
Exactly one of sdk_key or data must be provided"SDKConfig requires exactly one of 'sdk_key' or 'data'; neither was provided" / "both were provided"
sdk_key, when supplied, must be a non-empty string"'sdk_key' must be a non-empty string"
data, when supplied, must be a dict / mapping"'data' must be a mapping/dict"
cache_level must be None or "low""'cache_level' must be one of (None, 'low')"
batch_size must be a positive integer"'batch_size' must be a positive integer"
auto_flush_interval_ms, when provided, must be a positive integer"'auto_flush_interval_ms' must be a positive integer or None"

Queue and tracking settings

The two tracking-queue settings live on SDKConfig — there is no separate TrackingConfig:

batch_size (default 10): controls when the in-process queue self-releases. Once the queue accumulates batch_size events, a batch-size release fires automatically. Set lower for low-latency workloads; set higher to amortize delivery cost in high-throughput services.

auto_flush_interval_ms (default None): opts into a daemonic timer that releases the queue on a fixed interval. Leave as None (explicit-flush-only) for Lambda, cron jobs, and other short-lived runtimes where a background thread is inappropriate. A value of 2000 flushes every 2 seconds.

Both settings interact: whichever trigger fires first releases the queue. A core.flush() call always works regardless of either setting.

See Queue Control for the full release-trigger matrix and per-runtime guidance.

is_direct_config property

SDKConfig.is_direct_config is True when data= was provided (no network). The SDK uses this internally to skip transport construction; you can use it in your own config helpers.


TransportConfig

Network settings for the SDK's bundled httpx-backed transport. Used only when SDKConfig.sdk_key is set — direct-config (data) initialization never constructs or uses a transport.

from convert_sdk import TransportConfig

transport = TransportConfig(
    base_url="https://cdn-4.convertexperiments.com",
    timeout=10.0,
    auth_secret=None,
    headers={},
    verify_tls=True,
)

Field reference

FieldTypeDefaultPurpose
base_urlstr"https://cdn-4.convertexperiments.com"HTTPS base URL of the config-serving endpoint. The transport appends /api/v1/config/{sdkKey} to form the full config route. Must use the https scheme — a non-HTTPS URL raises TransportError at construction, before any network I/O (NFR8: TLS-only transport).
timeoutfloat10.0Per-request timeout in seconds, applied to both config-fetch and tracking delivery.
auth_secretstr | NoneNoneOptional bearer secret sent as an Authorization: Bearer <secret> header on config-fetch and tracking requests. Never logged.
headersMapping[str, str]{}Optional extra headers appended to every config-fetch request.
verify_tlsboolTrueWhether the httpx client verifies TLS certificates. Set to False only for development against a local HTTPS proxy.

TLS enforcement

A non-HTTPS base_url raises TransportError at TransportConfig construction — before Core.initialize() is called and before any network I/O is possible. This is a hard requirement (NFR8) that cannot be bypassed by supplying a custom transport at the SDKConfig level.

from convert_sdk import TransportConfig, TransportError

try:
    t = TransportConfig(base_url="http://insecure.example.com")
except TransportError as exc:
    print("rejected:", exc)
# TransportError: transport base_url must use HTTPS (TLS-only transport, NFR8); got scheme='http'

RefreshConfig

Background config-refresh policy for long-running remote (sdk_key) instances. Opt-in: SDKConfig.refresh=None (the default) keeps the SDK in MVP behavior — no daemon thread, no periodic re-fetch.

from convert_sdk import RefreshConfig

refresh = RefreshConfig(
    interval_seconds=300.0,     # base poll period (5 minutes)
    jitter_seconds=30.0,        # max random jitter per cycle
    backoff_factor=2.0,         # exponential backoff multiplier on failure
    backoff_max_seconds=600.0,  # backoff ceiling (10 minutes)
)

Field reference

FieldTypeDefaultPurpose
interval_secondsfloat300.0Base period (in seconds) between successful refresh attempts. Mirrors the JS SDK dataRefreshInterval default of 300 000 ms expressed in seconds. Must be > 0.
jitter_secondsfloat30.0Maximum uniform random jitter added to each scheduled wait, so a fleet of processes does not synchronize on the same refresh instant ("thundering herd" mitigation). Must satisfy 0 <= jitter_seconds <= interval_seconds.
backoff_factorfloat2.0Multiplier applied to the wait after each consecutive transient failure (exponential backoff). Must be >= 1.0. 1.0 means no backoff (constant retry interval).
backoff_max_secondsfloat600.0Ceiling on the backed-off wait. Ensures a persistently failing endpoint is retried at a bounded cadence rather than with exponentially growing pauses. Must be >= interval_seconds.

Validation rules

All validation runs in RefreshConfig.__post_init__ and raises InvalidConfigError:

RuleCondition
interval_seconds > 0Non-positive or non-numeric value
0 <= jitter_seconds <= interval_secondsNegative jitter, or jitter exceeds interval
backoff_factor >= 1.0Less than 1.0
backoff_max_seconds >= interval_secondsLess than interval

Behavior summary

  • The background worker starts when Core.initialize() is called with a RefreshConfig present and sdk_key mode active.
  • Each refresh re-fetches config through the same transport used at init.
  • A successful fetch atomically swaps the immutable snapshot under a mutex — see ADR 0001.
  • On every successful swap the SDK emits LifecycleEvent.CONFIG_UPDATED.
  • A transient failure leaves the prior snapshot in place; the worker backs off exponentially up to backoff_max_seconds.
  • Persistent failures are logged through the diagnostic logger (never crash the host process).
  • The worker is a daemon thread and never blocks interpreter exit.
  • Core.close() stops the worker cleanly.
  • Supplying a RefreshConfig in direct-config (data) mode starts no worker and logs a refresh.skipped diagnostic.

Recommended configurations

Unit tests / CI

from convert_sdk import SDKConfig

config = SDKConfig(
    data={
        "account_id": "1001",
        "project": {"id": "2002", "name": "Demo"},
        "experiences": [],
        "features": [],
        "goals": [],
    }
)
# No network, no background threads, deterministic.

Short-lived script or cron job

import os
from convert_sdk import SDKConfig

config = SDKConfig(
    sdk_key=os.environ["CONVERT_SDK_KEY"],
    environment="production",
    # refresh=None — process exits before a single interval would tick.
    # flush() explicitly at the end of the script.
)

Long-running web service (Django / Flask / FastAPI)

import os
from convert_sdk import SDKConfig, RefreshConfig

config = SDKConfig(
    sdk_key=os.environ["CONVERT_SDK_KEY"],
    environment="production",
    batch_size=25,
    refresh=RefreshConfig(
        interval_seconds=300.0,
        jitter_seconds=30.0,
        backoff_factor=2.0,
        backoff_max_seconds=600.0,
    ),
)
# Larger batch_size amortises per-request flush cost.
# Auto-refresh keeps long-lived processes from serving stale config.

Opt-in periodic flush for a long-lived background worker

import os
from convert_sdk import SDKConfig

config = SDKConfig(
    sdk_key=os.environ["CONVERT_SDK_KEY"],
    batch_size=50,
    auto_flush_interval_ms=5000,   # flush every 5 seconds
)

What to read next

  • Initialization — narrative on init modes, context-manager lifecycle, refresh
  • Code Examples — practical patterns using each option
  • Type Hints — full dataclass field types and enums
  • Extending — custom Transport, DataStore, and event-bus
  • Testing — test patterns using direct-config mode