# splitch > Feature flags and A/B experimentation with agent parity: every capability a person has in the panel is available to an agent over MCP, the CLI, and the SDK. Failures are always observable and never disguised as a default. Every page below is also served as HTML at the same URL without the `.md` suffix. ## Flags - [Flags](https://splitch.dev/docs/flags.md): Per-Environment Configuration: enabled, rollout, availableVariantNames, and Targeting Rules. ## SDK - [Install](https://splitch.dev/docs/sdk/install.md): Add @splitch/sdk and get one Flag resolving. - [Credentials](https://splitch.dev/docs/sdk/credentials.md): Client Key evaluates, API Key peeks. Pass exactly one. - [The four methods](https://splitch.dev/docs/sdk/methods.md): Which calls fire an Exposure, and which credential each needs. - [idempotencyKey](https://splitch.dev/docs/sdk/idempotency.md): One key per logical evaluation. Reuse it to retry safely. - [Failure behavior](https://splitch.dev/docs/sdk/failures.md): Evaluation never throws and never hides. Peek throws. - [Exposure dedup](https://splitch.dev/docs/sdk/exposure-dedup.md): Repeat evaluations replay locally. Run boundaries do not. - [Options](https://splitch.dev/docs/sdk/options.md): endpoint, timeoutMs, retries, logger, transport. ## Errors Every failure code the API, SDK, and CLI can emit resolves to a page at https://splitch.dev/docs/error/{code}. - [ACTIVATION_TIMESTAMP_INVALID](https://splitch.dev/docs/error/ACTIVATION_TIMESTAMP_INVALID.md): 400. An activation timestamp was placed at or before the first Exposure it would filter, which would silently drop the Entities the Run already measured. - [ALLOCATION_INVALID](https://splitch.dev/docs/error/ALLOCATION_INVALID.md): 400. The Variant allocation percentages on a Run do not sum to 100. - [APPROVAL_APPLICATION_FAILED](https://splitch.dev/docs/error/APPROVAL_APPLICATION_FAILED.md): 409. The Review was authorized but applying the change failed and rolled back. Nothing was written, and the request is still pending. - [APPROVAL_REQUEST_NOT_FOUND](https://splitch.dev/docs/error/APPROVAL_REQUEST_NOT_FOUND.md): 404. No Approval Request matched the identifier in the request, within the scope your credential can reach. - [APPROVAL_REQUEST_RESOLVED](https://splitch.dev/docs/error/APPROVAL_REQUEST_RESOLVED.md): 409. A different Review already resolved this request. - [APPROVAL_REQUEST_STALE](https://splitch.dev/docs/error/APPROVAL_REQUEST_STALE.md): 409. The target changed after the request was proposed, so approving it would apply a diff computed against state that no longer exists. The request is terminal. - [APPROVAL_REVIEW_FORBIDDEN](https://splitch.dev/docs/error/APPROVAL_REVIEW_FORBIDDEN.md): 403. This principal may not perform this Review. - [APPROVAL_REVIEW_REQUIRED](https://splitch.dev/docs/error/APPROVAL_REVIEW_REQUIRED.md): 409. The Environment Policy gates this change at `confirm`, and the call carried no inline `review`. A durable Approval Request now exists and is pending. - [APP_MISMATCH](https://splitch.dev/docs/error/APP_MISMATCH.md): 403. The credential belongs to a different App than the one the request addressed. - [APP_NOT_FOUND](https://splitch.dev/docs/error/APP_NOT_FOUND.md): 404. No App matched the identifier in the request, within the scope your credential can reach. - [ATTENTION_FANOUT_LIMIT_EXCEEDED](https://splitch.dev/docs/error/ATTENTION_FANOUT_LIMIT_EXCEEDED.md): 409. The App-wide attention rollup spans more Environments and running Experiments than it will read in one pass. It issues one Analysis read per running Experiment per Environment, and past the budget it refuses the whole read. - [CLI_API_ORIGIN_MISSING](https://splitch.dev/docs/error/CLI_API_ORIGIN_MISSING.md): CLI. The platform target in `SPLITCH_PLATFORM_TARGET` has no API origin configured in the environment. - [CLI_CONFIG_READ_FAILED](https://splitch.dev/docs/error/CLI_CONFIG_READ_FAILED.md): CLI. `.splitch/config.json` exists but could not be read or parsed. - [CLI_CREDENTIAL_STORE_FAILED](https://splitch.dev/docs/error/CLI_CREDENTIAL_STORE_FAILED.md): CLI. The credential store could not be read, written, or cleared. - [CLI_DATA_PLANE_ERROR_CODE_MISSING](https://splitch.dev/docs/error/CLI_DATA_PLANE_ERROR_CODE_MISSING.md): CLI. `splitch flags verify` got `reason: "ERROR"` from the data plane with no error code attached, so there is nothing to attribute the failure to. - [CLI_DEVICE_APPROVAL_TIMEOUT](https://splitch.dev/docs/error/CLI_DEVICE_APPROVAL_TIMEOUT.md): CLI. The device code expired before it was approved in the browser. - [CLI_DEVICE_AUTHORIZATION_FAILED](https://splitch.dev/docs/error/CLI_DEVICE_AUTHORIZATION_FAILED.md): CLI. The device authorization request failed, or the auth service returned a response missing a required field. - [CLI_DEVICE_TOKEN_EXCHANGE_FAILED](https://splitch.dev/docs/error/CLI_DEVICE_TOKEN_EXCHANGE_FAILED.md): CLI. The device code was approved but exchanging it for a token failed, or the token response carried no `user_id` to identify the session. - [CLI_EMAIL_UNVERIFIED](https://splitch.dev/docs/error/CLI_EMAIL_UNVERIFIED.md): CLI. The identity provider has not verified an email address for this account. - [CLI_LOGOUT_REVOKE_FAILED](https://splitch.dev/docs/error/CLI_LOGOUT_REVOKE_FAILED.md): CLI. The local credential was removed but the server refused to revoke the session, so the token may still be accepted elsewhere until it expires. - [CLI_NOT_AUTHENTICATED](https://splitch.dev/docs/error/CLI_NOT_AUTHENTICATED.md): CLI. No CLI login session is available. - [CLI_OPERATION_UNKNOWN](https://splitch.dev/docs/error/CLI_OPERATION_UNKNOWN.md): CLI. The command resolved to an operation id that is not in the registry. - [CLI_ROUTE_SURFACE_UNSUPPORTED](https://splitch.dev/docs/error/CLI_ROUTE_SURFACE_UNSUPPORTED.md): CLI. The operation the command maps to has no public origin the CLI can address. - [CLI_SCOPE_UNRESOLVED](https://splitch.dev/docs/error/CLI_SCOPE_UNRESOLVED.md): CLI. The App, Environment, or Flag the command addresses could not be pinned down: nothing matched the selector, more than one thing matched it, or an Environment was named without an App. - [CLI_SERVER_CODE_UNRECOGNIZED](https://splitch.dev/docs/error/CLI_SERVER_CODE_UNRECOGNIZED.md): CLI. The server returned an error code this CLI build does not know. The message is passed through verbatim. - [CLI_SESSION_EXPIRED](https://splitch.dev/docs/error/CLI_SESSION_EXPIRED.md): CLI. A login session exists but could not mint a usable token. - [CLI_TOKEN_BINDING_REFUSED](https://splitch.dev/docs/error/CLI_TOKEN_BINDING_REFUSED.md): CLI. The session token could not be bound to the requested scope, usually because the account holds no membership that reaches it. - [CLI_UNEXPECTED_ERROR](https://splitch.dev/docs/error/CLI_UNEXPECTED_ERROR.md): CLI. An unhandled fault inside the CLI. The underlying message is passed through. - [CLI_USAGE_INVALID](https://splitch.dev/docs/error/CLI_USAGE_INVALID.md): CLI. The invocation did not parse: an unknown command or flag, a flag missing its value, a required positional missing or supplied twice, an out-of-range value, or malformed `--body-json`. - [CLI_VALIDATION_ERROR](https://splitch.dev/docs/error/CLI_VALIDATION_ERROR.md): CLI. The invocation parsed but its input failed contract validation before any request was sent. `splitch flags create` without a variant catalog, `--variants` without `--key`, or an unrecognized `SPLITCH_PLATFORM_TARGET` all land here. - [CREDENTIAL_NOT_FOUND](https://splitch.dev/docs/error/CREDENTIAL_NOT_FOUND.md): 404. No credential matched the identifier in the request, within the scope your credential can reach. - [CREDENTIAL_REVOKED](https://splitch.dev/docs/error/CREDENTIAL_REVOKED.md): 403. The credential is well-formed and known, but has been revoked. - [DECISION_LOCKED](https://splitch.dev/docs/error/DECISION_LOCKED.md): 409. A decision-family or alpha setting was edited on a running Run. These fix the statistical test, and moving them mid-Run invalidates the result. - [EXPERIMENT_KEY_CONFLICT](https://splitch.dev/docs/error/EXPERIMENT_KEY_CONFLICT.md): 409. An Experiment already holds this `(App, Environment, key)`. Archiving an Experiment does not free its key, so the holder may be archived rather than live. - [EXPERIMENT_NOT_FOUND](https://splitch.dev/docs/error/EXPERIMENT_NOT_FOUND.md): 404. No Experiment matched the identifier in the request, within the scope your credential can reach. - [EXPERIMENT_NO_DRAFT](https://splitch.dev/docs/error/EXPERIMENT_NO_DRAFT.md): 409. Start was called but the draft is identical to the Run already live. Starting it would open a second Run that measures the same configuration, splitting one cohort across two Runs for no reason. - [EXPERIMENT_RUNNING](https://splitch.dev/docs/error/EXPERIMENT_RUNNING.md): 409. The operation is blocked while the Experiment has a live Run. Delete is the common one. - [FLAG_NOT_FOUND](https://splitch.dev/docs/error/FLAG_NOT_FOUND.md): 404. No Flag matched the identifier in the request, within the scope your credential can reach. - [FORBIDDEN](https://splitch.dev/docs/error/FORBIDDEN.md): 403. The principal is authenticated but is not authorized for this resource under its Organization role. - [IDEMPOTENCY_KEY_CONFLICT](https://splitch.dev/docs/error/IDEMPOTENCY_KEY_CONFLICT.md): 409. The same idempotency key was reused with a different canonical payload. Honoring it would let one key stand for two different changes. - [INSUFFICIENT_SCOPES](https://splitch.dev/docs/error/INSUFFICIENT_SCOPES.md): 403. The credential is valid but does not carry the scopes this operation requires. - [INTERNAL_SERVER_ERROR](https://splitch.dev/docs/error/INTERNAL_SERVER_ERROR.md): 500. An unhandled fault on the platform side. Nothing about the request was at fault. - [INVALID_PAGINATION](https://splitch.dev/docs/error/INVALID_PAGINATION.md): 400. The `cursor` or `limit` on a list request could not be used. - [INVALID_SORT](https://splitch.dev/docs/error/INVALID_SORT.md): 400. The requested sort field is not sortable on this collection. - [LAST_ENVIRONMENT_REQUIRED](https://splitch.dev/docs/error/LAST_ENVIRONMENT_REQUIRED.md): 409. The delete would leave an App with no Environment, and an App cannot serve without one. - [LAST_OWNER_REQUIRED](https://splitch.dev/docs/error/LAST_OWNER_REQUIRED.md): 409. The change would leave a shared Organization with no owner, which would strand every member without an escalation path. - [METRIC_NOT_FOUND](https://splitch.dev/docs/error/METRIC_NOT_FOUND.md): 404. No Metric matched the identifier in the request, within the scope your credential can reach. - [MULTIPLE_VARIANT_CONFLICT](https://splitch.dev/docs/error/MULTIPLE_VARIANT_CONFLICT.md): 409. One Entity was observed under more than one Variant in the same Run, so it is bucketed to `__multiple__` and its contribution is untrusted. - [ORGANIZATION_NOT_FOUND](https://splitch.dev/docs/error/ORGANIZATION_NOT_FOUND.md): 404. No Organization matched the identifier in the request, within the scope your credential can reach. - [ORIGIN_NOT_ALLOWED](https://splitch.dev/docs/error/ORIGIN_NOT_ALLOWED.md): 403. A valid Client Key was presented from an origin that is not on that key's allow-list. - [PRIVACY_CONFIRMATION_REQUIRED](https://splitch.dev/docs/error/PRIVACY_CONFIRMATION_REQUIRED.md): 409. A destructive privacy job was submitted without confirmation. These erase subject data irreversibly across every store. - [PRIVACY_JOB_FAILED](https://splitch.dev/docs/error/PRIVACY_JOB_FAILED.md): 500. A privacy job did not complete against every store, so subject data may remain in the ones it could not reach. - [PRIVACY_JOB_NOT_FOUND](https://splitch.dev/docs/error/PRIVACY_JOB_NOT_FOUND.md): 404. No privacy job matched the identifier in the request, within the scope your credential can reach. - [RATE_LIMITED](https://splitch.dev/docs/error/RATE_LIMITED.md): 429. The caller exceeded the rate budget for this surface. - [RESOURCE_NOT_EMPTY](https://splitch.dev/docs/error/RESOURCE_NOT_EMPTY.md): 409. A destructive delete was refused because child resources still exist under the target and this delete does not cascade. - [RUN_FROZEN](https://splitch.dev/docs/error/RUN_FROZEN.md): 409. The edit touches a field that a running Run freezes. Changing it mid-flight would rebucket Entities that were already measured, so the write is refused whole. - [RUN_NOT_FOUND](https://splitch.dev/docs/error/RUN_NOT_FOUND.md): 404. No Run matched the identifier in the request, within the scope your credential can reach. - [RUN_NOT_RUNNING](https://splitch.dev/docs/error/RUN_NOT_RUNNING.md): 409. An operation that only a live Run supports (End, for example) was called on a Run that is still a draft or already ended. - [SDK_CACHED_TELEMETRY_FAILED](https://splitch.dev/docs/error/SDK_CACHED_TELEMETRY_FAILED.md): SDK. A cached-evaluation telemetry report could not be delivered. - [SDK_CREDENTIAL_CONFIGURATION_INVALID](https://splitch.dev/docs/error/SDK_CREDENTIAL_CONFIGURATION_INVALID.md): SDK. `createSplitchClient` was given no credential, or both a `clientKey` and an `apiKey`. - [SDK_RETRIES_INVALID](https://splitch.dev/docs/error/SDK_RETRIES_INVALID.md): SDK. `retries` was set to anything other than `0`. - [SDK_SEEN_SET_MAX_SIZE_INVALID](https://splitch.dev/docs/error/SDK_SEEN_SET_MAX_SIZE_INVALID.md): SDK. The seen-set `maxSize` is not a positive integer. - [SDK_SEEN_SET_TTL_INVALID](https://splitch.dev/docs/error/SDK_SEEN_SET_TTL_INVALID.md): SDK. The seen-set TTL is not a positive duration. - [SDK_TRANSPORT_NETWORK](https://splitch.dev/docs/error/SDK_TRANSPORT_NETWORK.md): SDK. The SDK's transport threw before receiving an HTTP response — for example a network failure, a cancelled request that was not a timeout, or a local `fetch` misconfiguration such as an unbound `Window.fetch`. - [SDK_TRANSPORT_PARSE](https://splitch.dev/docs/error/SDK_TRANSPORT_PARSE.md): SDK. The transport received a response body that could not be parsed as the expected evaluate/peek/verify shape. - [SDK_TRANSPORT_TIMEOUT](https://splitch.dev/docs/error/SDK_TRANSPORT_TIMEOUT.md): SDK. The per-call request timeout elapsed (or the request was aborted) before a response arrived. - [SEGMENT_NOT_FOUND](https://splitch.dev/docs/error/SEGMENT_NOT_FOUND.md): 404. No Segment matched the identifier in the request, within the scope your credential can reach. - [SERVICE_UNAVAILABLE](https://splitch.dev/docs/error/SERVICE_UNAVAILABLE.md): 503. The edge returned HTTP 503: Provider configuration could not be resolved. This is retryable and does not mean your Flag is misconfigured. - [SLUG_CONFLICT](https://splitch.dev/docs/error/SLUG_CONFLICT.md): 409. The Organization slug is already taken. Slugs are a global handle, so the winner may be an Organization you cannot see. - [TARGETING_KEY_MISMATCH](https://splitch.dev/docs/error/TARGETING_KEY_MISMATCH.md): 409. The Targeting Key changed on a Run that is already assigning traffic. Every Entity would rebucket, and the Exposures already recorded would describe a different population than the ones that follow. - [UNAUTHORIZED](https://splitch.dev/docs/error/UNAUTHORIZED.md): 401. No valid credential was presented, or the one presented could not be parsed. - [USER_NOT_FOUND](https://splitch.dev/docs/error/USER_NOT_FOUND.md): 404. No user matched the identifier in the request, within the scope your credential can reach. - [VALIDATION_ERROR](https://splitch.dev/docs/error/VALIDATION_ERROR.md): 400. The request body, path, or query failed contract validation at the Worker boundary. - [VARIANT_NOT_AVAILABLE](https://splitch.dev/docs/error/VARIANT_NOT_AVAILABLE.md): 409. A referenced Variant is not promoted into this Environment. Flag definitions are App-level, but the set of Variants servable in a given Environment is per-Environment. - [VARIANT_NOT_FOUND](https://splitch.dev/docs/error/VARIANT_NOT_FOUND.md): 404. No Variant matched the identifier in the request, within the scope your credential can reach. ## Optional - [Quickstart](https://splitch.dev/quickstart): zero to a resolving Flag with the CLI. - [MCP server](https://mcp.splitch.dev): the same capabilities in-band for agents. - [Control Panel](https://app.splitch.dev)