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

# Declarations

> A declaration is everything a secret-key call says about an element: the control, the arms, and which field changes start a new experiment.

Every secret-key call that names an element is a complete **declaration** of how that element should be optimized. The first declaration for an element creates its **experiment**. There is no separate "create experiment" step and no experiment id to manage: you declare, and Curious keeps the chain.

## The three outcomes of a declaration

* **Identical declaration:** nothing happens. This is the normal case. Your code sends the same declaration on every call, and Curious compares it to what is running.
* **A changed declaration-class field:** the running experiment is concluded with reason `superseded` and a new one begins from the new declaration. These fields define what the variants were generated against, so changing one invalidates what has been learned.
* **A changed mutable field:** the running experiment is updated in place. These guide future generation or serving policy without invalidating existing variants.

| Field | Class | Why |
| - | - | - |
| `control` (its content) | Supersedes | Every variant is measured against it. |
| `rewards` | Supersedes | The model's structure is fixed by what success means. |
| `format` | Supersedes | A different shape makes every existing arm unservable. |
| `requirements` | Supersedes | Arms were generated under the old phrase rules. |
| `parameters` | Supersedes | Arm content restates the declared facts. |
| `options.holdout` | Supersedes | The held-out cohort must be constant for an experiment's life. |
| `constraints` | In place | Guardrails apply from the next evaluation. |
| `instructions` | In place | Guidance for future variants only. |
| `variables` | In place | Rendering configuration with fallbacks. |
| `options.assignment`, `options.approval`, `options.minimum_effect_size` | In place | Serving and review policy. |
| `arms` | Adds | New starting points join the experiment; existing ones are untouched. |
| `element_type` | In place | Shapes future generation. |

An omitted declaration-class field carries forward from the running experiment, so a call that sends only `element` and `user_id` never supersedes anything. A control is required whenever a call creates an experiment and there is no running one to carry it from, which is the first call for an element; on a supersession the running control carries forward unless the change is the control itself. Unknown fields anywhere in a declaration, including inside nested objects, are a `400` naming the path, so a typo cannot silently become a no-op.

## The control and the arms

The content Curious can serve for an element are its **arms**.

The **control** is yours: the version you show today, bare content or `{ "content": …, "hypothesis": … }`. It is the counterfactual every variant is measured against, the content served while variants are being generated, the content held-out users always see, and the content served when nothing else can be. The control is never concluded by any path, which is why a response's `content` is never null.

**Variants** are generated by Curious in rounds, each with a hypothesis about why it should win, and tested against the control and against each other. You may seed variants of your own with `arms`: one entry or a list, each bare content or `{content, hypothesis}`. Declared arms are additive and idempotent: an entry the experiment already has is skipped, and resending the list changes nothing. An entry equal to the control's content, or more than 10 distinct entries in one call, is a `400`. An entry that duplicates an earlier one in the same call, violates the format or the requirements, or would exceed the 50 active arms an experiment may hold is skipped with an `arm_invalid` warning in the response rather than failing the call.

Every decision response names the arm served, `arm_id`, and whether it was the control or a variant, `arm_type`.

## Where a declaration lives

Send it inline on every `decide`, or move it to a setup step with [`declare`](https://docs.trycurious.ai/guides/declare) and let the serving code carry only `element` and `user_id`. Both paths run the same validation and the same transaction. Whichever you choose, keep the declaration in one place: a declaration-class field that differs between two call sites supersedes the experiment on every alternation. See [The experiment restarted](https://docs.trycurious.ai/troubleshooting/experiment-restarted.md).


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