Compares the resolved flag's configuration between two environments (`base` and `other`, key or id), side by side: each side carries the flag's environment config and targeting rules, and `ruleHasMatchInOther` marks per rule whether the other environment has an equal one.
const url = 'https://api.featureflip.io/api/v1/orgs/example/projects/example/flags/example/compare';const options = {method: 'GET', headers: {Authorization: '<Authorization>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.featureflip.io/api/v1/orgs/example/projects/example/flags/example/compare \ --header 'Authorization: <Authorization>'A side is null where
the flag has no configuration. Open to any role. Routed as {flag}/compare so a flag keyed
compare stays addressable.
Authorizations
Section titled “ Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “ Path Parameters ”Query Parameters
Section titled “ Query Parameters ”Responses
Section titled “ Responses ”OK
Response of GET .../flags/{flag}/compare.
object
One environment’s side of a per-flag comparison.
object
Public representation of a flag’s config within a single environment, returned by
GET .../flags/{flag}/environments and GET .../flags/{flag}/environments/{env}.
Drops internal/dashboard-only fields present on FlagEnvironmentConfigurationDto
— Id (an internal row id, not part of the public
contract; EnvironmentId is the addressable key), FeatureFlagId (implicit
in the route), the environment’s name/color/sort-order (dashboard display fields — the public
contract only needs the addressable EnvironmentKey), and Staleness (an
internal analytics signal).
object
Allowed values: SingleVariation, PercentageRollout, TargetedRollout.
A single prerequisite entry on a flag’s per-environment config. Maps 1:1 onto PrerequisiteDto (read side) and PrerequisiteInput (write side).
object
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
The full targeting configuration for a flag in a single environment, returned by
GET .../targeting. Maps 1:1 onto TargetingConfigurationDto — including
its IsEnabled field, which reflects the parent
FlagEnvironmentConfiguration.IsEnabled (whether the flag is on in this environment at
all), not anything specific to targeting itself.
object
A single targeting rule, returned by GET .../rules/{ruleId},
POST .../rules (201 body), and nested within PublicTargetingConfigResponse.
Maps 1:1 onto TargetingRuleDto, dropping nothing — every field on the
Application DTO is part of the public contract.
object
Context attribute the rollout hashes on (default user_id; userId is accepted as an alias). A context without it skips this rule, even at 100%. Lookup is exact and case-sensitive, except user_id/userId, which both read the user id.
A condition group within a PublicTargetingRuleResponse. Maps 1:1 onto
ConditionGroupDto. Operator is a bare wire string naming one of
the ConditionGroupOperator members ("And"/"Or") —
groups are ANDed together at the rule level; within a group, its conditions combine using
this operator.
object
Allowed values: And, Or.
A single condition within a PublicConditionGroupResponse. Maps 1:1 onto
ConditionDto. Operator is a bare wire string naming one of the
OperatorType members (e.g. "Equals", "In").
object
Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
A flag’s configuration in one environment, as a comparison or copy result reports it.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
A targeting rule as a comparison or copy shows it: the variation and segment with key and name, and the condition groups in the shape the targeting endpoints return.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
A condition group within a PublicTargetingRuleResponse. Maps 1:1 onto
ConditionGroupDto. Operator is a bare wire string naming one of
the ConditionGroupOperator members ("And"/"Or") —
groups are ANDed together at the rule level; within a group, its conditions combine using
this operator.
object
Allowed values: And, Or.
A single condition within a PublicConditionGroupResponse. Maps 1:1 onto
ConditionDto. Operator is a bare wire string naming one of the
OperatorType members (e.g. "Equals", "In").
object
Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.
A single prerequisite entry on a flag’s per-environment config. Maps 1:1 onto PrerequisiteDto (read side) and PrerequisiteInput (write side).
object
Per rule in evaluation order: whether the other environment has an equal rule.
One environment’s side of a per-flag comparison.
object
Public representation of a flag’s config within a single environment, returned by
GET .../flags/{flag}/environments and GET .../flags/{flag}/environments/{env}.
Drops internal/dashboard-only fields present on FlagEnvironmentConfigurationDto
— Id (an internal row id, not part of the public
contract; EnvironmentId is the addressable key), FeatureFlagId (implicit
in the route), the environment’s name/color/sort-order (dashboard display fields — the public
contract only needs the addressable EnvironmentKey), and Staleness (an
internal analytics signal).
object
Allowed values: SingleVariation, PercentageRollout, TargetedRollout.
A single prerequisite entry on a flag’s per-environment config. Maps 1:1 onto PrerequisiteDto (read side) and PrerequisiteInput (write side).
object
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
The full targeting configuration for a flag in a single environment, returned by
GET .../targeting. Maps 1:1 onto TargetingConfigurationDto — including
its IsEnabled field, which reflects the parent
FlagEnvironmentConfiguration.IsEnabled (whether the flag is on in this environment at
all), not anything specific to targeting itself.
object
A single targeting rule, returned by GET .../rules/{ruleId},
POST .../rules (201 body), and nested within PublicTargetingConfigResponse.
Maps 1:1 onto TargetingRuleDto, dropping nothing — every field on the
Application DTO is part of the public contract.
object
Context attribute the rollout hashes on (default user_id; userId is accepted as an alias). A context without it skips this rule, even at 100%. Lookup is exact and case-sensitive, except user_id/userId, which both read the user id.
A condition group within a PublicTargetingRuleResponse. Maps 1:1 onto
ConditionGroupDto. Operator is a bare wire string naming one of
the ConditionGroupOperator members ("And"/"Or") —
groups are ANDed together at the rule level; within a group, its conditions combine using
this operator.
object
Allowed values: And, Or.
A single condition within a PublicConditionGroupResponse. Maps 1:1 onto
ConditionDto. Operator is a bare wire string naming one of the
OperatorType members (e.g. "Equals", "In").
object
Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller’s role and the resource’s state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.
object
One entry in a resource’s _actions block: may the caller perform it, and if not, why.
object
A flag’s configuration in one environment, as a comparison or copy result reports it.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
A targeting rule as a comparison or copy shows it: the variation and segment with key and name, and the condition groups in the shape the targeting endpoints return.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
An id with its key and name beside it, as the other v1 responses pair them. Key and name are null only for a deleted target.
object
A condition group within a PublicTargetingRuleResponse. Maps 1:1 onto
ConditionGroupDto. Operator is a bare wire string naming one of
the ConditionGroupOperator members ("And"/"Or") —
groups are ANDed together at the rule level; within a group, its conditions combine using
this operator.
object
Allowed values: And, Or.
A single condition within a PublicConditionGroupResponse. Maps 1:1 onto
ConditionDto. Operator is a bare wire string naming one of the
OperatorType members (e.g. "Equals", "In").
object
Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.
A single prerequisite entry on a flag’s per-environment config. Maps 1:1 onto PrerequisiteDto (read side) and PrerequisiteInput (write side).
object
Per rule in evaluation order: whether the other environment has an equal rule.
Example
{ "base": { "config": { "environmentId": "0197b69f-2b3c-7d4e-9f5a-6b7c8d9e0f1a", "environmentKey": "production", "isEnabled": true, "defaultVariationId": "0197b6a0-6a1b-7d4f-8c2e-3a4b5c6d7e8f", "strategy": "PercentageRollout", "prerequisites": [ { "prerequisiteFlagKey": "checkout-api-enabled", "expectedVariationKey": "true" } ] }, "targeting": { "rules": [ { "id": "0197b6a3-5e6f-7a7b-8c8d-9e0f1a2b3c4d", "priority": 0, "description": "North American users get the new flow at 25%.", "variationId": "0197b6a0-6a1b-7d4f-8c2e-2f3a4b5c6d7e", "rolloutPercentage": 25, "bucketBy": "user_id", "userSegmentId": "0197b6a1-3c4d-7e5f-8a6b-7c8d9e0f1a2b", "conditionGroups": [ { "operator": "And", "conditions": [ { "attribute": "country", "operator": "In", "values": [ "US", "CA" ], "negate": false } ] } ] } ] }, "snapshot": { "rules": [ { "conditionGroups": [ { "operator": "And", "conditions": [ { "operator": "Equals" } ] } ] } ] } }, "other": { "config": { "environmentId": "0197b69f-2b3c-7d4e-9f5a-6b7c8d9e0f1a", "environmentKey": "production", "isEnabled": true, "defaultVariationId": "0197b6a0-6a1b-7d4f-8c2e-3a4b5c6d7e8f", "strategy": "PercentageRollout", "prerequisites": [ { "prerequisiteFlagKey": "checkout-api-enabled", "expectedVariationKey": "true" } ] }, "targeting": { "rules": [ { "id": "0197b6a3-5e6f-7a7b-8c8d-9e0f1a2b3c4d", "priority": 0, "description": "North American users get the new flow at 25%.", "variationId": "0197b6a0-6a1b-7d4f-8c2e-2f3a4b5c6d7e", "rolloutPercentage": 25, "bucketBy": "user_id", "userSegmentId": "0197b6a1-3c4d-7e5f-8a6b-7c8d9e0f1a2b", "conditionGroups": [ { "operator": "And", "conditions": [ { "attribute": "country", "operator": "In", "values": [ "US", "CA" ], "negate": false } ] } ] } ] }, "snapshot": { "rules": [ { "conditionGroups": [ { "operator": "And", "conditions": [ { "operator": "Equals" } ] } ] } ] } }}Headers
Section titled “ Headers ”The maximum number of requests permitted per rate-limit window for this caller.
The number of requests remaining in the current rate-limit window.
The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.
Bad Request
The frozen public-API error contract. error codes are stable snake_case strings. Wire keys
are frozen snake_case too (error, message, docs_url, fields,
retry_after) — the global camelCase naming policy would otherwise emit
docsUrl/retryAfter, breaking the published spec. !:JsonPropertyName
always wins over the policy, so these are pinned explicitly rather than relying on the
property names already being lowercase for the single-word ones.
did_you_mean, next_actions and connection_id are ADDITIVE optional keys
(null → omitted via the global DefaultIgnoreCondition = WhenWritingNull), so
pre-existing error bodies are byte-for-byte unchanged when they’re absent.
connection_id is present only on the sso_required error: the organization
requires single sign-on, and this credential didn’t come through its identity provider.
connection_id identifies which SSO connection to sign in with.
plan_limit is present only on the plan_limit_reached error: which plan cap the
request would have exceeded, so a client can branch on it without parsing message.
object
object
object
The cap behind a plan_limit_reached error. resource is the capped collection
(projects, environments, flags, webhooks, members);
plan is the plan’s wire name, as GET /api/v1/orgs/{org} returns it; max is
the cap. The environment and flag caps are per project.
object
Example
{ "error": "not_found", "message": "Flag 'new-checkout-flw' was not found in project 'checkout'.", "docs_url": "https://featureflip.io/docs/management-api/errors/not_found", "did_you_mean": [ "new-checkout-flow" ], "next_actions": [ { "method": "GET", "path": "/api/v1/orgs/acme/projects/checkout/flags" } ]}Headers
Section titled “ Headers ”The maximum number of requests permitted per rate-limit window for this caller.
The number of requests remaining in the current rate-limit window.
The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.
Authentication is required, or the supplied API token is invalid or expired.
The frozen public-API error contract. error codes are stable snake_case strings. Wire keys
are frozen snake_case too (error, message, docs_url, fields,
retry_after) — the global camelCase naming policy would otherwise emit
docsUrl/retryAfter, breaking the published spec. !:JsonPropertyName
always wins over the policy, so these are pinned explicitly rather than relying on the
property names already being lowercase for the single-word ones.
did_you_mean, next_actions and connection_id are ADDITIVE optional keys
(null → omitted via the global DefaultIgnoreCondition = WhenWritingNull), so
pre-existing error bodies are byte-for-byte unchanged when they’re absent.
connection_id is present only on the sso_required error: the organization
requires single sign-on, and this credential didn’t come through its identity provider.
connection_id identifies which SSO connection to sign in with.
plan_limit is present only on the plan_limit_reached error: which plan cap the
request would have exceeded, so a client can branch on it without parsing message.
object
object
object
The cap behind a plan_limit_reached error. resource is the capped collection
(projects, environments, flags, webhooks, members);
plan is the plan’s wire name, as GET /api/v1/orgs/{org} returns it; max is
the cap. The environment and flag caps are per project.
object
Example
{ "error": "not_found", "message": "Flag 'new-checkout-flw' was not found in project 'checkout'.", "docs_url": "https://featureflip.io/docs/management-api/errors/not_found", "did_you_mean": [ "new-checkout-flow" ], "next_actions": [ { "method": "GET", "path": "/api/v1/orgs/acme/projects/checkout/flags" } ]}Headers
Section titled “ Headers ”The maximum number of requests permitted per rate-limit window for this caller.
The number of requests remaining in the current rate-limit window.
The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.
Not Found
The frozen public-API error contract. error codes are stable snake_case strings. Wire keys
are frozen snake_case too (error, message, docs_url, fields,
retry_after) — the global camelCase naming policy would otherwise emit
docsUrl/retryAfter, breaking the published spec. !:JsonPropertyName
always wins over the policy, so these are pinned explicitly rather than relying on the
property names already being lowercase for the single-word ones.
did_you_mean, next_actions and connection_id are ADDITIVE optional keys
(null → omitted via the global DefaultIgnoreCondition = WhenWritingNull), so
pre-existing error bodies are byte-for-byte unchanged when they’re absent.
connection_id is present only on the sso_required error: the organization
requires single sign-on, and this credential didn’t come through its identity provider.
connection_id identifies which SSO connection to sign in with.
plan_limit is present only on the plan_limit_reached error: which plan cap the
request would have exceeded, so a client can branch on it without parsing message.
object
object
object
The cap behind a plan_limit_reached error. resource is the capped collection
(projects, environments, flags, webhooks, members);
plan is the plan’s wire name, as GET /api/v1/orgs/{org} returns it; max is
the cap. The environment and flag caps are per project.
object
Example
{ "error": "not_found", "message": "Flag 'new-checkout-flw' was not found in project 'checkout'.", "docs_url": "https://featureflip.io/docs/management-api/errors/not_found", "did_you_mean": [ "new-checkout-flow" ], "next_actions": [ { "method": "GET", "path": "/api/v1/orgs/acme/projects/checkout/flags" } ]}Headers
Section titled “ Headers ”The maximum number of requests permitted per rate-limit window for this caller.
The number of requests remaining in the current rate-limit window.
The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.
Rate limit exceeded. Retry after the interval indicated by the Retry-After header.
The frozen public-API error contract. error codes are stable snake_case strings. Wire keys
are frozen snake_case too (error, message, docs_url, fields,
retry_after) — the global camelCase naming policy would otherwise emit
docsUrl/retryAfter, breaking the published spec. !:JsonPropertyName
always wins over the policy, so these are pinned explicitly rather than relying on the
property names already being lowercase for the single-word ones.
did_you_mean, next_actions and connection_id are ADDITIVE optional keys
(null → omitted via the global DefaultIgnoreCondition = WhenWritingNull), so
pre-existing error bodies are byte-for-byte unchanged when they’re absent.
connection_id is present only on the sso_required error: the organization
requires single sign-on, and this credential didn’t come through its identity provider.
connection_id identifies which SSO connection to sign in with.
plan_limit is present only on the plan_limit_reached error: which plan cap the
request would have exceeded, so a client can branch on it without parsing message.
object
object
object
The cap behind a plan_limit_reached error. resource is the capped collection
(projects, environments, flags, webhooks, members);
plan is the plan’s wire name, as GET /api/v1/orgs/{org} returns it; max is
the cap. The environment and flag caps are per project.
object
Example
{ "error": "not_found", "message": "Flag 'new-checkout-flw' was not found in project 'checkout'.", "docs_url": "https://featureflip.io/docs/management-api/errors/not_found", "did_you_mean": [ "new-checkout-flow" ], "next_actions": [ { "method": "GET", "path": "/api/v1/orgs/acme/projects/checkout/flags" } ]}Headers
Section titled “ Headers ”Number of seconds to wait before retrying the request.
The maximum number of requests permitted per rate-limit window for this caller.
The number of requests remaining in the current rate-limit window.
The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.