Skip to content

Creates a targeting rule on the resolved flag in the resolved environment.

POST
/api/v1/orgs/{org}/projects/{project}/flags/{flag}/environments/{env}/rules
curl --request POST \
--url https://api.featureflip.io/api/v1/orgs/example/projects/example/flags/example/environments/example/rules \
--header 'Authorization: <Authorization>' \
--header 'Content-Type: application/json' \
--data '{ "description": "North American users get the new flow at 25%.", "variationId": "0197b6a0-6a1b-7d4f-8c2e-2f3a4b5c6d7e", "rolloutPercentage": 25, "conditionGroups": [ { "operator": "And", "conditions": [ { "attribute": "country", "operator": "In", "values": [ "US", "CA" ], "negate": false } ] } ] }'

Requires at least Member. Re-fetches via GetTargetingRuleQuery using the id returned from CreateTargetingRuleCommand so the response reflects exactly what was persisted (e.g. server-assigned priority and condition/group ids).

org
required
string
project
required
string
flag
required
string
env
required
string

Request body for POST .../rules. The Application-side CreateTargetingRuleDto/CreateConditionGroupDto/CreateConditionDto all take their Operator as a bare string, re-parsed CASE-SENSITIVELY inside CreateTargetingRuleCommandHandlerFlagTargetingController’s mapping helpers validate every operator against the enum’s member-name allowlist AND canonicalize the casing (e.g. "in""In") before building the command DTO, so a valid case-insensitive match never trips the handler’s strict re-parse.

object
description
string
nullable
variationId
string format: uuid
rolloutPercentage
integer format: int32
nullable
userSegmentId
string format: uuid
nullable
conditionGroups
Array<object>
nullable

Request-side condition group, symmetric with PublicConditionGroupResponse.

object
operator

Allowed values: And, Or.

string
nullable
Allowed values: And Or
conditions
Array<object>
nullable

Request-side condition, symmetric with PublicConditionResponse.

object
attribute
string
nullable
operator

Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.

string
nullable
Allowed values: Equals NotEquals Contains NotContains GreaterThan LessThan GreaterThanOrEqual LessThanOrEqual In NotIn MatchesRegex StartsWith EndsWith Before After SemverEquals SemverGreaterThan SemverGreaterThanOrEqual SemverLessThan SemverLessThanOrEqual
values
Array<string>
nullable
negate
boolean
Example
{
"description": "North American users get the new flow at 25%.",
"variationId": "0197b6a0-6a1b-7d4f-8c2e-2f3a4b5c6d7e",
"rolloutPercentage": 25,
"conditionGroups": [
{
"operator": "And",
"conditions": [
{
"attribute": "country",
"operator": "In",
"values": [
"US",
"CA"
],
"negate": false
}
]
}
]
}

Created

Media typeapplication/json

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
id
string format: uuid
priority
integer format: int32
description
string
nullable
variationId
string format: uuid
rolloutPercentage
integer format: int32
nullable
userSegmentId
string format: uuid
nullable
conditionGroups
Array<object>
nullable

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
operator

Allowed values: And, Or.

string
nullable
Allowed values: And Or
conditions
Array<object>
nullable

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
attribute
string
nullable
operator

Allowed values: Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, In, NotIn, MatchesRegex, StartsWith, EndsWith, Before, After, SemverEquals, SemverGreaterThan, SemverGreaterThanOrEqual, SemverLessThan, SemverLessThanOrEqual.

string
nullable
Allowed values: Equals NotEquals Contains NotContains GreaterThan LessThan GreaterThanOrEqual LessThanOrEqual In NotIn MatchesRegex StartsWith EndsWith Before After SemverEquals SemverGreaterThan SemverGreaterThanOrEqual SemverLessThan SemverLessThanOrEqual
values
Array<string>
nullable
negate
boolean
_actions

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
key
additional properties

One entry in a resource’s _actions block: may the caller perform it, and if not, why.

object
allowed
boolean
reason
string
nullable
Example
{
"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,
"userSegmentId": "0197b6a1-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
"conditionGroups": [
{
"operator": "And",
"conditions": [
{
"attribute": "country",
"operator": "In",
"values": [
"US",
"CA"
],
"negate": false
}
]
}
]
}
X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.

Bad Request

Media typeapplication/json

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 and next_actions 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.

object
error
string
nullable
message
string
nullable
docs_url
string
nullable
fields
object
key
additional properties
Array<string>
retry_after
integer format: int32
nullable
did_you_mean
Array<string>
nullable
next_actions
Array<object>
nullable
object
method
string
nullable
path
string
nullable
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"
}
]
}
X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

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.

Media typeapplication/json

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 and next_actions 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.

object
error
string
nullable
message
string
nullable
docs_url
string
nullable
fields
object
key
additional properties
Array<string>
retry_after
integer format: int32
nullable
did_you_mean
Array<string>
nullable
next_actions
Array<object>
nullable
object
method
string
nullable
path
string
nullable
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"
}
]
}
X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.

Forbidden

Media typeapplication/json

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 and next_actions 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.

object
error
string
nullable
message
string
nullable
docs_url
string
nullable
fields
object
key
additional properties
Array<string>
retry_after
integer format: int32
nullable
did_you_mean
Array<string>
nullable
next_actions
Array<object>
nullable
object
method
string
nullable
path
string
nullable
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"
}
]
}
X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.

Not Found

Media typeapplication/json

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 and next_actions 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.

object
error
string
nullable
message
string
nullable
docs_url
string
nullable
fields
object
key
additional properties
Array<string>
retry_after
integer format: int32
nullable
did_you_mean
Array<string>
nullable
next_actions
Array<object>
nullable
object
method
string
nullable
path
string
nullable
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"
}
]
}
X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

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.

Media typeapplication/json

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 and next_actions 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.

object
error
string
nullable
message
string
nullable
docs_url
string
nullable
fields
object
key
additional properties
Array<string>
retry_after
integer format: int32
nullable
did_you_mean
Array<string>
nullable
next_actions
Array<object>
nullable
object
method
string
nullable
path
string
nullable
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"
}
]
}
Retry-After
integer

Number of seconds to wait before retrying the request.

X-RateLimit-Limit
integer

The maximum number of requests permitted per rate-limit window for this caller.

X-RateLimit-Remaining
integer

The number of requests remaining in the current rate-limit window.

X-RateLimit-Reset
integer

The UTC time at which the current rate-limit window resets, as a Unix timestamp in seconds.