# Feature flags in your application

> Have your agent reuse or create a flag, read it on your server, take the real treatment branch, record the exposure, and prove that the exact decision arrived under the right definition version.

Canonical page: https://anectico.com/docs/instrument/feature-flags/


A feature flag decides which branch of your code a person gets. Your agent wires that decision into
your server, records an **exposure** each time the application uses a decision, and then asks Anectico
whether the exposures of a controlled test arrived with the value and flag definition version the
application reported. This page is the instrumentation workflow. [Check rollout health](/docs/investigate/rollout-health)
covers flag configuration, rollouts and measuring exposures later.

## What this covers

| Path | Covered |
| --- | --- |
| Node.js server with the Anectico JavaScript SDK, remote decisions | Yes |
| Node.js server with the SDK, local evaluation | Yes, for person-property targeting only |
| A server in any language, remote decisions over HTTP | One tested recipe. See [Integrate without an SDK](/docs/instrument/without-an-sdk). |
| Browser or mobile application | Separate client-delivery workflow; see [browser flags](/docs/instrument/javascript#feature-flags-in-the-browser) and [mobile flags and headless surveys](/docs/instrument/mobile#remote-flags-and-headless-surveys). The server sample verifier below does not qualify those SDK integrations. |
| Go and Python servers with their SDKs | Not by this workflow. Their flag clients record only the earlier exposure fields, so the exact checks below cannot verify them. |

A browser or mobile app evaluates flags with a client delivery key that holds only `flags:evaluate`
([from a page or app](/docs/investigate/rollout-health#from-a-page-or-app)); this workflow does not yet
integrate or verify that path. The flags key this page uses reads every flag definition of the
project, so it must never reach a browser or an app.

## Ask your agent

> Add the `new-checkout` flag to my checkout service. Reuse it if it exists. Serve the new branch to
> the pro plan, record the exposure, run the service as two people, and verify exactly what arrived.

| Job | MCP tool or action | CLI command |
| --- | --- | --- |
| Read existing flags | `list_flags`, `get_flag` | `anectico flags list`, `anectico flags get` |
| Create a flag, after a preview you approve | `create_flag` | `anectico flags create` |
| Learn what Anectico decides for a known person and the flag's current definition version | `decide_feature_flags` | `anectico flags decide` |
| See exactly what a client application gets for named keys | `evaluate_client_flags` | `anectico flags evaluate` |
| Download the local-evaluation snapshot | `get_local_evaluation_snapshot` | `anectico flags snapshot` |
| Verify the recorded exposures | `verify_flag_integration`, `verify_integration` | `anectico doctor flags`, `anectico doctor integration` |

Flag reads need `flags:read` and flag writes need `flags:write`. A flag your agent decides itself
checks Anectico's evaluation, never your application, and it is never evidence that the application
called the flag.

## The flag

Your agent reads before it writes. It reuses a flag that exists and creates one only when none fits
and you approved the plan. The preview shows the complete proposed definition; after you approve it,
the agent repeats the identical arguments with the confirmation token and reads the flag back. A valid
definition, a preview and a creation receipt are configuration: none of them shows that your
application reads the flag. Changing a flag that already exists is a different write with its own
version check and preview.

## Credentials and addresses

| Setting | What it is |
| --- | --- |
| Decision address | The address that serves `POST /api/v1/decide` and the local-evaluation snapshot. |
| Capture address | The address that serves `POST /api/v1/capture`, where exposures are sent. |
| Flags key | A project-bound management key with only `flags:read`, held only in server secret configuration. |
| Capture key | A `server_capture` key with only `analytics:write`. It must be a different key from the flags key. |
| Project | Required for local evaluation, which is bound to one project. Remote decisions take it from the key. |

Your agent's own key is a third credential that only the verification uses. See [Ask your agent to
integrate Anectico](/docs/start/ask-your-agent-to-integrate#credentials-three-never-interchangeable)
and [Native intake keys](/docs/instrument/native-intake-keys).

A decision uses the person properties your application sends with the request. A key that holds only
`flags:read` does not read the person's stored profile properties, so the application sends every
property the flag's rules use.

## What the application does on every read

1. Takes the person from the server's own session, never from the request body.
2. Supplies every property the flag's rules use.
3. Asks for the decision: with the SDK, `reload` and `getFeatureFlagDetails` on a request-scoped
   `FlagsClient` for remote evaluation, or `refresh` once and `evaluate` on a `LocalFlagsClient` kept
   for the process; over HTTP, `POST /api/v1/decide`. The reference is
   [JavaScript flag-client reference](/docs/reference/javascript-sdk#feature-flags-anecticosdkanalytics).
4. **Serves the branch from the value.** The served value decides the code path, not a log line.
5. Sends one exposure to the capture address with the capture key, through the exposure callback.
   Derive its message ID from the request and the flag so a retry keeps the ID, and pass a test-case
   ID on the controlled run.
6. When no decision can be read, serves the application's safe default and sends no exposure.

The [Node example](/docs/instrument/integration-examples#the-node-example) keeps all of this in one
file so the integration is the difference between the application before and after.

## What a read means

| What happened | What the application serves | Exposure | What it is |
| --- | --- | --- | --- |
| Evaluated and matched | The treatment | Yes, `evaluated`, not a fallback | A decision |
| Evaluated and not matched, value `false` or a control variant | The control | Yes, `evaluated`, not a fallback | A valid decision. `false` is a value. |
| The request failed, the answer was not 200, or the flag is missing | The application's default | None | An unavailable decision. It is not `false`. |
| The server answered with a fallback after an evaluation error | The default value | Remote: yes, with `evaluation_error`, `is_fallback: true` and no version | A fallback, not a decision. The one-verdict job verifies only successful use; check a fallback with `anectico doctor flags`. |
| Local evaluation with a stale snapshot, an unknown flag, or a cohort, group or saved-audience target | No value | None | Unavailable or `unsupported_targeting`. Evaluate that flag remotely. |

Local evaluation supports person-property targeting only. The snapshot omits flags that target saved
audiences, so a flag omitted from it is not an inactive flag.

## The exposure the application records

Each served read records a `$feature_flag_called` event whose reserved properties say what was used:
the flag key, the value, the exact definition version (only for a successful evaluation, never for
a fallback), the evaluation status, whether the value is a fallback, `remote` or `local`, the local
snapshot version, your test-case ID, and an input context. The JavaScript SDK records them for you;
an application without the SDK sends them itself. Every property and the exact form of the input
binding are on [Feature-flag exposure verification](/docs/reference/flag-integration-verification).

An exposure is **application-reported use**. It never proves that a person saw the treatment, and it
says nothing about a code path that records no exposure.

## Versions, inputs and the local and remote pair

- The version is the flag **definition's** version. It does not version person properties, groups,
  cohorts or audiences.
- The input context says whether the evaluation used only inputs your application supplied
  (`explicit_inputs`), also used stored context (`hydrated_context`), or is unknown
  (`unestablished`). The input binding is your application's own declaration of its explicit inputs.
  Anectico compares two bindings for equality; it never recomputes or authenticates them.
- A local and a remote decision are comparable only when exactly one of each was selected, both were
  successful non-fallback evaluations under the same version, both used explicit inputs with every
  targeting input present, and their bindings are equal. Then the answer says whether the values
  `agree` or `disagree`. Otherwise it is `not_comparable` with reasons. A remote decision that used
  stored context is `context_not_established`: today's context is never reread as proof of the
  context at evaluation time. `not_comparable` does not mean the treatments differ.

## Prove the exact decision

Run the application as a person the flag matches and one it does not, so the run contains a
successful `true` and a successful `false`. Keep the exposure message IDs, the served branch, the
version Anectico returns for the flag, and the capture key's source ID. Then ask for the `flags` job:

```bash
anectico doctor integration --file verify-flags-job.json -o json
```

The request, the checks and the states are in [Integration job
verification](/docs/reference/integration-job-verification). The verdict is `verified_sample` only
when every selected exposure matched the value, version, status, fallback flag, mode and input
context the application reported, under the declared identity. The scopes are `query:read`,
`analytics:read` and `flags:read`, plus `persons:read` for identity and `agents:content:read` for the
recorded fields; without those two the checks are `withheld`.

A verified exposure proves that the application recorded that decision. It does not prove that
every flag read records an exposure, that other people were treated the same way, or that anyone saw
the change. How long to wait, the bounds and the known gaps are on [What integration verification
proves and what it does not](/docs/reference/integration-verification-limits).

## Not covered yet

The server workflow's exact-sample qualification does not cover browser and mobile flag clients,
Go and Python flag exposures, or flags that rely on stored person, group or cohort context for a
verified local and remote comparison. Use the linked client guides for their implemented APIs and
delivery checks. [Product experiments](/docs/investigate/product-experiments) need isolated runs of their own.

## Next

- [Ask your agent to integrate Anectico](/docs/start/ask-your-agent-to-integrate)
- [Plan and prove product events](/docs/instrument/product-analytics)
- [Runnable integration examples](/docs/instrument/integration-examples)
