Shipeasy
SDKsReferenceKotlin

Configuration

Configure the SDK once at app boot with configure(...), then evaluate per user/request with Client(user).

Generated from the SDK's own /docs/ — also served raw at https://shipeasy-ai.github.io/sdk-kotlin/pages/configuration.md.

Configure the SDK once at app boot with configure(...), then evaluate per user/request with Client(user).

configure(...)

import ai.shipeasy.configure
import ai.shipeasy.Client

configure(
    apiKey = System.getenv("SHIPEASY_SERVER_KEY"),
    attributes = { u -> mapOf("user_id" to (u as MyUser).id, "plan" to u.plan) },
)

configure() builds the process-global SDK state (HTTP client + blob cache + optional poll) and registers the attributes transform. The first call wins; later calls are ignored and leave the transform untouched, so configure exactly once.

Options

ParameterDefaultWhat it does
apiKeySERVER key — authenticates flags/experiments/SSR.
attributesidentityYour user object → attribute map. Runs once per Client(user).
baseUrlhttps://api.shipeasy.aiEdge API origin override.
env"prod"Tags telemetry + see() events; also the fallback for the egress defaults (below).
isNetworkEnablednull (env-derived)Master switch on all outbound requests. null ⇒ on in production, off elsewhere.
isTrackingEnablednull (env-derived)Usage-telemetry switch. null ⇒ on in production, off elsewhere. Forced off when the network is off.
disableTelemetryfalseLegacy hard opt-out of per-eval usage telemetry (equivalent to isTrackingEnabled = false).
telemetryUrlnullOverride the telemetry beacon origin.
privateAttributes[]Attrs usable for targeting but stripped from outbound payloads.
stickyStorenullLock a unit to its first-assigned variant.
pollfalsetrue → fetch once and keep polling; false → one-shot fetch.
logLevelLogLevel.WARNSDK log verbosity (SILENT, ERROR, WARN, INFO, DEBUG).
disableInternalErrorReportingfalseOpt out of self-reporting SDK-internal errors to Shipeasy.
clientKeynullPublic client key (sdk_client_…) — the default the SSR i18n / devtools tags carry. Never the server key.
profilenull (⇒ "en:prod")Default i18n profile the SSR tags carry.
projectIdnullProject id (proj_…) read by devtoolsScriptTag.
cdnBaseUrlhttps://cdn.shipeasy.aiCDN origin the SSR tags are built against.

The full options table with types lives on the Installation page — that page is the canonical home for configure().

Use the SERVER key. It authenticates flag, experiment and SSR evaluation and must never reach the browser. The public client key is only used by the i18n loader / bootstrap script tags (see Advanced / i18n).

The attributes transform

attributes: (Any?) -> Map<String, Any?> maps YOUR user object into the targeting bag every evaluation reads (user_id, anonymous_id, plus targeting attributes). It runs once per Client(user) construction.

With no transform, the identity default is used — if the user object is already a Map, it IS the attribute bag:

configure(apiKey = System.getenv("SHIPEASY_SERVER_KEY"))
Client(mapOf("user_id" to "u_123", "plan" to "pro")).getFlag("new_checkout")

Identity / anonymous default

When the bound attributes carry neither user_id nor anonymous_id, the SDK defaults anonymous_id to the request-scoped __se_anon_id cookie (resolved by AnonIdFilter, see Advanced). An explicit unit always wins.

One-shot vs polling

By default configure() fetches the rule blob once so the first Client(user).getFlag(...) resolves against real rules. For a long-running server that should also poll for updates in the background, pass poll = true:

configure(apiKey = System.getenv("SHIPEASY_SERVER_KEY"), poll = true)

With poll = true the SDK does the first fetch then refreshes in the background (interval driven by the server's X-Poll-Interval header, default 30s). Register an onChange listener to react to each refresh.

Fail-safe reads & the logLevel option

Runtime reads never throw into your request path. getFlag, getFlagDetail, getConfig, universe(name).assign(), getKillswitch and the fire-and-forget track / see() calls each catch any unexpected error, log it, and return the documented safe default (flag → your default, config → your default, assign → not-enrolled Assignment that resolves get() to the universe default/your fallback, killswitch → false, track → no-op). So an evaluation problem degrades gracefully instead of taking down the request.

Setup and lifecycle calls stay loud on purpose — constructing Client(user) before configure(), configureForOffline(...) with no source, or a bad snapshot path still throw, because those are boot-time misconfiguration you want surfaced.

When one of these last-resort guards catches an internal SDK failure — a bug on our side, not yours — the SDK also reports it to Shipeasy's own project so we can find and fix SDK bugs across the apps that run it. This never touches your project or your Errors tab, carries no user/app data beyond the error itself, and is fire-and-forget (it can never slow down or break a read). It is on by default; opt out with disableInternalErrorReporting = true:

configure(
    apiKey = System.getenv("SHIPEASY_SERVER_KEY"),
    disableInternalErrorReporting = true,
)

Test/offline mode (configureForTesting / configureForOffline) never sends anything, so internal reporting is off there regardless.

Control how much the SDK logs with logLevel (default WARN), ordered SILENT < ERROR < WARN < INFO < DEBUG — a message at level L is emitted only when the configured level is >= L:

import ai.shipeasy.LogLevel

configure(
    apiKey = System.getenv("SHIPEASY_SERVER_KEY"),
    logLevel = LogLevel.SILENT,   // mute the SDK entirely
)

Logging goes through java.util.logging under the logger name "shipeasy".

Network & telemetry: quiet outside production

Since 0.16.0 the SDK is offline by default outside production. Both egress controls — isNetworkEnabled (the master switch on every outbound request: flag/experiment fetch, poll, track, exposure, see(), and usage telemetry) and isTrackingEnabled (just the usage beacon) — default to on in production and off in every other environment. So running an app that embeds the SDK on a dev machine or in CI never phones home unless you opt in; when the network is off, reads return your in-code defaults / overrides and nothing is sent.

"Is this production?" is resolved with this precedence:

  1. A native runtime signal, in order: the shipeasy.env system property, then the SHIPEASY_ENV, APP_ENV, ENV environment variables. A value of production or prod (case-insensitive) ⇒ production; any other present value (staging, test, …) ⇒ not production.
  2. If none of those is set (common on serverless / mobile), fall back to the SDK's own env option — which already defaults to "prod", so a real production deploy stays on by default while env = "dev" stays quiet.

An explicitly-passed value always wins over the default:

// Force the SDK fully online even on a dev box (e.g. an integration test that
// really should hit the edge):
configure(apiKey = System.getenv("SHIPEASY_SERVER_KEY"), isNetworkEnabled = true)

// Or keep flags/experiments flowing but suppress the usage beacon:
configure(apiKey = System.getenv("SHIPEASY_SERVER_KEY"), isTrackingEnabled = false)

Restoring the pre-0.16.0 "always on" behaviour: either pass isNetworkEnabled = true (and, if you also want the usage beacon, isTrackingEnabled = true), or mark the environment as production — -Dshipeasy.env=production on the JVM, or export SHIPEASY_ENV=production.

configureForTesting / configureForOffline are unaffected — they were already fully offline.

Environment variables

Apart from the egress signals above (SHIPEASY_ENV / APP_ENV / ENV, all optional), the SDK reads no env vars implicitly — pass apiKey (and any baseUrl) explicitly. By convention the key lives in SHIPEASY_SERVER_KEY.

Was this page helpful?
Updated July 26, 2026

On this page