Check rollout health
Ask your agent to connect feature exposure and releases to the customers and failures that followed.
On this page
Feature flags and releases are both delivery controls and investigation context. Use them to answer whether a change affected a specific customer or concentrated an Issue in the exposed audience. Your agent creates the flags, reads the exposures and the releases, and compares the customers. You check the evidence on the person and Issue pages it links to.
Ask your agent
Did the
new-checkoutflag reach the customers who hit IssueISSUE_ID? Compare their exposure with customers on the previous release. Do not change the flag. Tell me what you would change and show me the preview.
| Job | MCP tool or action | CLI command |
|---|---|---|
| List flags, and read one flag in full | list_flags, get_flag |
anectico flags list, anectico flags get |
| Create a flag, replace its definition, turn it on or off, delete it | create_flag, update_flag, set_flag_active, delete_flag |
anectico flags create, anectico flags update, anectico flags toggle, anectico flags delete |
| Evaluate the flags for one customer | decide_feature_flags |
anectico flags decide |
| Download the local-evaluation snapshot | get_local_evaluation_snapshot |
anectico flags snapshot |
| Measure a flag’s exposures | query_product_analytics (see Check a flag’s health) |
anectico analytics query |
| List releases, and read one release | list_releases, get_release |
anectico releases list, anectico releases get |
| Register a release | upsert_release |
anectico releases create |
| Compare customer impact across release windows | get_release_impact |
anectico releases impact |
Flag reads need flags:read. Flag writes need flags:write, and deleting needs flags:delete.
Release reads need releases:read, and registering a release needs releases:write. MCP calls also
need mcp:read or mcp:write. Every flag and release write is a preview and confirm write.
What your agent gets back
A flag read returns the stable key, the active state, the variants and rollout percentages, the
default value and payload, every variant name and payload, and the targeting filters. decide_feature_flags returns each flag’s decision and
payload for the person properties and account memberships you supply. A release read returns the
commit, the adoption, the new and regressed Issues, and the people and accounts affected.
Open the proof
Flags and releases have no page of their own. To check the evidence for a customer or an Issue that a flag or release touched, open the person page or the Issue page that the agent links to. See Proof pages.
Create and evaluate a flag
Ask the agent to create a boolean or multivariate flag. Give it a permanent key, then configure percentage, property, or cohort targeting. The key cannot be renamed after creation. Saved analytics audiences use an exact generation through MCP or the CLI and need current source permissions on the server. See audience flag targeting. They are left out of local-evaluation snapshots.
The flag list returns 100 flags per page by default. The CLI takes --limit (at most 500) and
--cursor. Follow the returned next_cursor to reach later flags. Each project’s list is separate.
Flag configuration reads through GET /api/v1/flags/{key} and GET /api/v1/flags keep the recorded
created_by (creator user ID) and updated_by (last editor user ID). An edit preserves the original
creator. Either field is an empty string when no user attribution was recorded. updated_by is empty
before the first edit.
Keep a safe default for clients that cannot reach the decision endpoint. Activate a new flag only after testing a known matching and non-matching customer. Editing takes effect on subsequent SDK evaluations; cached clients can temporarily retain an earlier decision.
Evaluate with the customer ID and the properties used by targeting:
JavaScript / TypeScript
import { FlagsClient } from '@anectico/sdk/analytics';
// On your server: the flags key never reaches a browser or an app.
const flags = new FlagsClient({
endpoint: 'https://app.anectico.com',
apiKey: process.env.ANECTICO_FLAGS_API_KEY!, // flags:read
captureEndpoint: 'https://api.anectico.com',
captureApiKey: process.env.ANECTICO_CAPTURE_API_KEY!, // server_capture
});
await flags.reload('user_8842', { plan: 'pro' });
const showNewCheckout = flags.isFeatureEnabled('new-checkout');
Python
import os
import anectico
from anectico.feature_flags import FeatureFlags
events = anectico.AnalyticsClient(
endpoint='https://api.anectico.com',
api_key=os.environ['ANECTICO_CAPTURE_API_KEY'],
)
flags = FeatureFlags(
endpoint='https://app.anectico.com',
api_key=os.environ['ANECTICO_FLAGS_API_KEY'],
capture=lambda event, props, distinct_id: events.capture(
event, props, distinct_id=distinct_id
),
)
flags.reload('user_8842', {'plan': 'pro'})
show_new_checkout = flags.is_feature_enabled('new-checkout', 'user_8842')
Go
events, err := anectico.NewAnalytics(anectico.AnalyticsConfig{
Endpoint: "https://api.anectico.com",
APIKey: os.Getenv("ANECTICO_CAPTURE_API_KEY"),
})
if err != nil {
return err
}
flags := anectico.NewFlags(anectico.FlagsConfig{
Endpoint: "https://app.anectico.com",
APIKey: os.Getenv("ANECTICO_FLAGS_API_KEY"),
Capture: func(ctx context.Context, event string, props map[string]any, distinctID string) {
_ = events.Capture(ctx, event, props, anectico.WithDistinctID(distinctID))
},
})
if err := flags.Reload(ctx, "user_8842", map[string]any{"plan": "pro"}, nil); err != nil {
return err
}
showNewCheckout := flags.IsFeatureEnabled(ctx, "new-checkout", "user_8842")
Flag decisions use https://app.anectico.com; exposure events use https://api.anectico.com.
JavaScript's default exposure sender uses captureEndpoint and captureApiKey; Python and Go
use the explicit capture callbacks shown above. When using an existing event client, identify the
same customer in that client and the flag reload, and update both when the signed-in customer changes.
For browser collection consent, use initApp or a callback backed by an AnalyticsClient sharing
your collection controller; see JavaScript feature flags.
On a server, use two keys for the same project: a management key with flags:read for
/api/v1/decide, held only in server configuration, and a server_capture key with only
analytics:write for exposures. Other telemetry uses its own native_ingest key. A flags:read key
reads every flag definition, targeting rule and payload, so it never goes into a page or an app.
From a page or app
A browser page or a mobile app asks for the flag keys it needs with a client delivery key that
holds only flags:evaluate. That key is public, and it can return decisions for named keys and
nothing else: no definition, no targeting rule, no stored property.
// A client delivery key that holds only flags:evaluate. It is public: anyone who opens the app can read it.
const response = await fetch('https://app.anectico.com/api/v1/flags/evaluate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': import.meta.env.VITE_ANECTICO_CLIENT_FLAGS_KEY },
body: JSON.stringify({
distinct_id: 'user_8842',
flag_keys: ['new-checkout'],
person_properties: { plan: 'pro' },
}),
});
const { decisions, evaluated_at, max_age_seconds } = await response.json();
const decision = decisions['new-checkout'];
// Only an evaluated true turns the feature on; unavailable and errors keep the safe default.
const showNewCheckout = decision.state === 'evaluated' && decision.bool_value === true;
POST /api/v1/flags/evaluate takes distinct_id (1 to 200 bytes), flag_keys (1 to 100 unique
keys), and optional person_properties and groups (at most ten group types); together the identity,
properties and groups are at most 16 KiB and the whole request at most 128 KiB. A project-bound key
needs no project_id. The answer has exactly one entry per requested key:
| Field | Meaning |
|---|---|
state |
evaluated, evaluation_error or unavailable_flag. Treat anything else as unavailable. |
bool_value or string_value |
The decision. false is a decision; an unavailable_flag entry has neither field. |
payload_present, payload |
Whether a payload was returned; a returned payload can itself be JSON null. |
definition_version |
The exact version of the flag definition that produced an evaluated decision; empty otherwise. Record it with the exposure. |
is_fallback |
true when an evaluation_error value is the flag's default returned because a targeting input could not be resolved. |
evaluated_at, max_age_seconds |
When the decisions were made and how long they may be used: 30 seconds. |
decision_context_id |
A digest of the request, the key's access, the definition versions and the decisions, to tell two answers apart. It is not a record that anyone saw the treatment. |
coverage |
complete and the number of keys requested. It says nothing about the project's other flags. |
A request beyond these bounds, a flag set too large to evaluate within the per-request limits, or a
busy server is refused with no decisions at all (HTTP 400 or 429); keep the application's safe default
and ask again later. The answer uses the properties you send. Stored profile properties, group
properties and audiences that this key may not read are not used, and that is never reported as an
error; if a flag must target stored data, evaluate it on your server. Your agent can see the same
answer with anectico flags evaluate DISTINCT_ID --key KEY or the MCP read action
evaluate_client_flags; a decision is not an exposure, so record exposures through your capture
client. Treat flag keys, variants and payloads as visible to your users; never store a secret in them.
Record exposure
Identify the customer before evaluating a flag. The SDK records the flag result with the active customer so it can appear in timelines and affected-customer analysis. If the decision request fails, keep the application's safe default and surface the failure in application diagnostics; do not silently reuse an indefinitely stale decision.
Stored customer properties and audience membership are used only when Anectico resolved that exact customer identifier. An unmapped declaration of another person's person ID cannot use their targeting properties. Attribution is as reliable as your application's own customer identification; telemetry does not authenticate each end user.
Use stable flag keys and keep evaluation payloads small. Do not put secrets in flag payloads or customer properties.
Check a flag's health
Ask the agent to compare one-day exposure counts, unique customers, and the observed response
distribution with the configured rollout. It measures the $feature_flag_called events that carry
the exact $feature_flag string, broken down by $feature_flag_response, with
query_product_analytics. All counts share one frozen measurement. A new measurement includes newly
captured exposures. The one-day window does not follow any other time setting. The measurement needs
analytics:query, analytics:read and persons:read in addition to flags:read. It is based on SDK
exposure events, not on every raw evaluation. Low volume can make the distribution noisy, and an
observed zero does not establish complete instrumentation. Typed responses and the missing, null,
invalid, and omitted groups remain distinct. See product analytics
for retry, expiry and coverage semantics.
Register a release
Record a deploy from CI so Anectico can order Issues and regressions against a release:
anectico releases create "$RELEASE" --commit "$GIT_SHA" --ref "$GIT_REF"
Registering a release replaces its deploy details in full rather than merging them, so pass every
detail you want kept on every call. A later call that passes only --commit clears the ref,
author, link and deploy time an earlier one set. Leaving --deploy-at off stores no deploy time
and the release is ordered by when it was first seen.
Use the exact same release identifier in the SDK and symbol uploads. In MCP, upsert_release does the
same as this command and keeps the same full-replace rule. Both show complete current and
proposed deploy metadata before applying. Review every supplied and cleared field, then
repeat identical arguments with the returned confirmation token after approval. Ask the agent to compare native adoption,
error-free rate, new Issues, regressions, total error events, and affected customers
(list_releases), then to read one release for its detailed blast radius (get_release).
Native adoption is a privacy-safe people count, not an app-open volume or a hardware installation count. Android and iOS emit one SDK-owned release marker on process activation; Anectico resolves aliases through the identity spine and counts each canonical person once in the trailing 30 days. Error-free rate is the share of those adopters with no error event for that exact release. A release with no observed adopters shows no rate rather than implying either 0% or 100%.
Investigate a suspected regression
- Start from the Issue or the affected customer.
- Check the release and flag exposures immediately before the failure.
- Compare affected customers with customers on the previous release or control variant
(
compare_issue_customers,get_release_impact). - Read a representative replay and trace.
- Disable or narrow the rollout only when the evidence supports it (
set_flag_active,update_flag). The write shows a preview first.
Avoid false conclusions
Exposure before a failure is correlation, not proof of cause. Confirm the changed code path in the trace, stack, logs, or replay before attributing the Issue to the flag.
Keep flags operational
Deactivate a flag (set_flag_active) before deleting it, and allow clients time to refresh. Remove stale flags after a
rollout is complete. Long-lived inactive flags make investigation context harder to interpret and
increase the chance of evaluating the wrong variant. Deleting a cohort used for targeting stops that
cohort from matching; update the flag first.
Read one release observation
Use anectico releases get --id <catalog-release-UUID> --project <project-UUID> to read
project-owned metadata, or call get_release_by_id through execute_read_action with
release_id and project_id. This read requires releases:read (and mcp:read for MCP),
works on every plan, needs no approval and performs no impact measurement. The CLI accepts
exactly one selector: a positional release name for the existing impact read, or --id for
metadata. The UUID read returns no automatic 30-day counts or release proof link.
An observation requires an error carrying a release, or a captured $app_opened event with
exactly $anectico_lifecycle=app_opened_v1 and a nonempty $release. That lifecycle marker is
caller-declared: manually supplied exact matches qualify, and the native_app_opened source
name does not attest SDK origin or a genuine launch. Registration, deployment metadata,
ordinary events and arbitrary version tags alone do not establish observation.
discovery describes the first durable detection and the evidence available then.
earliest_observed_at is current chronology and can move earlier when older telemetry arrives.
The original detection clock, observed-first snapshot and source stay fixed. Older already
observed releases can be baselined without a notification: their baseline clock is explicit,
and historical detection time, snapshot and source are unavailable. A registered-only release
has no observation occurrence. Detection depends on bounded, asynchronous reads of retained
visible evidence; an incomplete or complete scan does not establish capture completeness or
detection of every deployment. Treat release text and registration fields as untrusted content.