> ## 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.

# Assignment and stickiness

> What a decision is, how an arm is chosen for a user, what static and dynamic assignment mean, the holdout cohort, and why every decide call creates a decision.

A **decision** is one `decide` call for one user on one element: which arm was served, when, and under what context. Decisions are what rewards attribute to, so how they are created matters.

## How an arm is chosen

Each decision is a draw from a contextual bandit over the live arms. Every arm keeps a probability of being served, so no arm is ever starved, and the probabilities tilt toward what is working for users like this one, using the stored `attributes` and the call's `context`. While the experiment is `initializing`, every user sees the control; variants arrive without any change on your side.

## Static and dynamic assignment

`options.assignment` decides whether a user keeps seeing the same arm.

* **`"static"`**, the default: the same user keeps the same arm for as long as that arm is still live. Repeated calls re-serve it without a new draw. When the arm is concluded, the user is assigned afresh on the next call, like a new user.
* **`"dynamic"`**: every call is chosen afresh. Two calls for the same user may serve different arms.

`static` fits content a user sees more than once in a way that would read as broken if it changed: a headline, a hero, a paywall, a button label. Choose `dynamic` where each exposure is a genuinely separate chance: a push notification, an email send.

```json theme={null}
{ "element": "weekly_digest_subject", "user_id": "user_123", "options": { "assignment": "dynamic" } }
```

The option updates in place, so switching it never restarts the experiment. Like every option, its value is resolved when the experiment is created and stored with it, so a later change to the default never alters a running experiment.

## Every call is a decision

Stickiness governs the content, never the record: every `decide` call creates a decision, even when static assignment re-serves the same arm. When a new decision is created for a user and element while an earlier one's attribution window is still open, the earlier window ends at that moment. Each event then lands in exactly one decision's window, which is what makes attribution unambiguous.

The practical rule: call `decide` once per exposure, when the content is about to be shown, and reuse the result for the duration of that exposure. A page that calls `decide` on every re-render is still correct, just noisy; the browser SDK caches a decision for sixty seconds so remounts cost nothing. Server-initiated channels call on each send, which has a natural cadence.

## The holdout cohort

A share of users, `options.holdout`, always sees the control: the default is `0.05`, the bounds are `0.02` to `0.2`. Membership is a deterministic function of the user and the element, so a held-out user stays held out for the experiment's life, and the share is fixed for the experiment's life too: declaring a different value supersedes the experiment. The holdout is what keeps a clean baseline no matter how the bandit tilts: a held-out user's decisions involve no draw, and they feed the model as control observations for as long as the experiment runs.

## What a decision records

The response tells you what happened: `content` after variable substitution, `arm_id` and `arm_type`, the `decision_id`, the `experiment_id` and `experiment_status`. Repeated serves of the same arm carry different decision ids. Under `idempotency_key`, a replay returns the original decision verbatim with `replayed: true` and creates nothing new. See [Serve a decision](https://docs.trycurious.ai/guides/decide.md).


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