Skip to main content
Going live is a key swap. Before you make it, confirm each line below; every one is something that has gone wrong for someone.

Keys

  • Servers read a live secret key from the environment (CURIOUS_SECRET_KEY), never from code. Browsers and mobile apps hold a live publishable key only.
  • The live publishable key’s origin allowlist lists every exact https:// origin that will call from a browser. A missing origin is a 403 for every visitor on it.
  • No generic CURIOUS_API_KEY variable in a browser bundle, where a build step could substitute the secret one.
  • You know how to rotate: create a new key, deploy it, revoke the old. Revocation takes effect within about thirty seconds. See Keys and environments.

The declaration

  • Each element’s declaration lives in one place: inline on every decide, or in a declare step, not both. Two call sites that disagree on a declaration-class field supersede the experiment on every alternation.
  • options.assignment is chosen: static for content a user sees repeatedly, dynamic for per-send channels. See Assignment and stickiness.
  • options.holdout is what you want; it cannot be changed without restarting the experiment.
  • options.approval is decided: manual (the default) means someone will review variants in the dashboard; auto means they serve as generated.
  • element_type is set where one fits, so variants are written with the right craft.

The delivery path

  • Every decide has a deadline and serves your control when it passes or anything fails. See Fallbacks and timeouts.
  • decide is called once per exposure, not on every render.
  • No decide retry without an idempotency_key.
  • You log X-Request-Id (the SDK’s requestId) and the fallback rate.

Events and identity

  • Every event your rewards and constraints name is reported, with the properties your filters match on, for every user.
  • Events carry a stable id, and retries reuse it.
  • Buffered or backfilled events carry their real timestamp.
  • The user_id on events is the one you decided with, and login calls identify from the server; logout mints a fresh anonymous id.
  • Queued events are flushed before the runtime exits or freezes (flush() handed to waitUntil on edge runtimes).

The first live decision

  • Serve one decision with the live key and find it in the dashboard under the element, with experiment_status: "initializing" if the element is new.
  • Report one event for that user and see it on the experiment.
  • If variants were already approved under the test key, note that the live experiment is the same chain: what you approved is what serves.

Afterwards

Watch the dashboard for the first variants, and read the hypothesis behind each. Rejections with a note make the next round better. If decisions keep returning the control after the experiment should be exploring, start at Every decision returns the control.