> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycurious.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Go-live checklist

> What to confirm before switching from a test key to a live one: keys and origins, the declaration, the delivery path, events, identity, the first decision.

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](https://docs.trycurious.ai/guides/keys.md).

## The declaration

* [ ] Each element's declaration lives in **one place**: inline on every `decide`, or in a [`declare`](https://docs.trycurious.ai/guides/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](https://docs.trycurious.ai/concepts/assignment.md).
* [ ] `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](https://docs.trycurious.ai/guides/fallbacks.md).
* [ ] `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`](https://docs.trycurious.ai/guides/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](https://app.trycurious.ai) 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](https://docs.trycurious.ai/troubleshooting/control-always-served.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.