# `PostHog.FeatureFlags`
[🔗](https://github.com/posthog/posthog-elixir/blob/v2.18.0/lib/posthog/feature_flags.ex#L1)

Convenience functions to work with Feature Flags API

# `check`

> This function is deprecated. Use PostHog.FeatureFlags.evaluate_flags/2 with PostHog.FeatureFlags.Evaluations.enabled?/2 or get_flag/2.

```elixir
@spec check(
  PostHog.supervisor_name(),
  String.t(),
  PostHog.distinct_id() | map() | nil
) ::
  {:ok, boolean()} | {:ok, String.t()} | {:error, Exception.t()}
```

Checks feature flag

> #### Deprecated {: .warning}
>
> Use `PostHog.FeatureFlags.evaluate_flags/2` plus
> `PostHog.FeatureFlags.Evaluations.enabled?/2` or
> `PostHog.FeatureFlags.Evaluations.get_flag/2` instead. The snapshot lets
> one `/flags` call serve multiple flag checks plus event enrichment.

If there is a variant assigned, returns `{:ok, variant}`. Otherwise, `{:ok,
true}` or `{:ok, false}`.

Accepts an optional `distinct_id` or a map with request body. If neither is
passed, attempts to read `distinct_id` from the context.

This function will also
[send](https://posthog.com/docs/api/flags#step-3-send-a-feature_flag_called-event)
`$feature_flag_called` event and
[set](https://posthog.com/docs/api/flags#step-2-include-feature-flag-information-when-capturing-events)
`$feature/feature-flag-name` property in context.

## Examples

Check boolean feature flag for `distinct_id`:

    iex> PostHog.FeatureFlags.check("example-feature-flag-1", "user123")
    {:ok, true}

Check multivariant feature flag for `distinct_id` in the current context:

    iex> PostHog.set_context(%{distinct_id: "user123"})
    iex> PostHog.FeatureFlags.check("example-feature-flag-1")
    {:ok, "variant1"}

Check boolean feature flag through a named PostHog instance:

    PostHog.FeatureFlags.check(MyPostHog, "example-feature-flag-1", "user123")

# `check!`

> This function is deprecated. Use PostHog.FeatureFlags.evaluate_flags/2 with PostHog.FeatureFlags.Evaluations.enabled?/2 or get_flag/2.

```elixir
@spec check!(
  PostHog.supervisor_name(),
  String.t(),
  PostHog.distinct_id() | map() | nil
) ::
  boolean() | String.t() | no_return()
```

Checks feature flag and returns the variant or raises on error.

> #### Deprecated {: .warning}
>
> Use `PostHog.FeatureFlags.evaluate_flags/2` and
> `PostHog.FeatureFlags.Evaluations.enabled?/2` /
> `PostHog.FeatureFlags.Evaluations.get_flag/2` instead.

This is a wrapper around `check/3` that returns the variant directly
or raises an exception on error. This follows the Elixir convention where
functions ending with `!` raise exceptions instead of returning error tuples.

> **Warning**: Use this function with care as it will raise an error if the feature flag
> is not found or if there are any API errors. For more resilient code, use `check/3`
> which returns `{:error, reason}` instead of raising.

## Examples

Check feature flag and get the variant:

    iex> PostHog.FeatureFlags.check!("example-feature-flag-1", "user123")
    true

Check multivariant feature flag for distinct_id in current context:

    iex> PostHog.set_context(%{distinct_id: "user123"})
    iex> PostHog.FeatureFlags.check!("example-feature-flag-1")
    "variant1"

Check feature flag through a named PostHog instance:

    iex> PostHog.FeatureFlags.check!(MyPostHog, "example-feature-flag-1", "user123")
    false

Raises an error when feature flag is not found:

    iex> PostHog.FeatureFlags.check!("example-feature-flag-3", "user123")
    ** (PostHog.UnexpectedResponseError) Feature flag example-feature-flag-3 was not found in the response

# `evaluate_flags`

```elixir
@spec evaluate_flags(PostHog.supervisor_name(), PostHog.distinct_id() | map() | nil) ::
  {:ok, PostHog.FeatureFlags.Evaluations.t()} | {:error, Exception.t()}
```

Evaluates feature flags for a `distinct_id` and returns a snapshot.

Returns `{:ok, %PostHog.FeatureFlags.Evaluations{}}` on success. Definitions
are evaluated locally first when local evaluation is configured with a
privileged key or a shared definition cache provider;
unresolved flags are filled by at most one `/flags` call. If that fallback
fails after some flags resolved locally, the successful local subset is
returned as a partial snapshot. If nothing resolved, the remote error is
returned to preserve the existing `{:error, reason}` contract. The snapshot
lets you branch on multiple flags and enrich
captured events — see
`PostHog.FeatureFlags.Evaluations` for the full snapshot API and
`set_in_context/2` for the recommended capture-enrichment flow.

Accepts an optional `distinct_id` or a request body map. If neither is
passed, attempts to read `distinct_id` from the context.

## Body options

When passing a map, the following keys are forwarded to the `/flags` request
body unchanged:

- `:distinct_id` (required, unless found in context)
- `:groups`
- `:person_properties`
- `:group_properties`
- `:device_id` - alternate bucketing identifier for device-bucketed flags

The public `:disable_geoip` option is sent under the `/flags` wire key
`geoip_disable`.

Plus these snapshot-specific options:

- `:only_evaluate_locally` - when true, never calls `/flags`; unresolved
  flags are omitted from the snapshot.

- `:flag_keys` - list of flag keys. A non-empty list is forwarded to the
  request as `flag_keys_to_evaluate` so the server returns only those flags.
  An empty list returns an empty snapshot without making a request. An
  omitted or `nil` value evaluates all flags through the normal request path.
  This scopes the network response, distinct from
  `PostHog.FeatureFlags.Evaluations.only/2` which filters an already-fetched
  snapshot in memory. Clean remote omissions for explicitly requested keys
  are retained in a bounded per-instance set until definitions refresh, so
  repeated and concurrent missing-key probes avoid duplicate requests.

## Examples

Evaluate flags for a `distinct_id`:

    {:ok, snapshot} = PostHog.FeatureFlags.evaluate_flags("user123")
    PostHog.FeatureFlags.Evaluations.enabled?(snapshot, "new-dashboard")

Evaluate a scoped set of flags with person properties:

    PostHog.FeatureFlags.evaluate_flags(%{
      distinct_id: "user123",
      person_properties: %{plan: "enterprise"},
      flag_keys: ["new-dashboard", "beta-checkout"]
    })

Evaluate through a named PostHog instance:

    PostHog.FeatureFlags.evaluate_flags(MyPostHog, "user123")

# `flags`

```elixir
@spec flags(PostHog.supervisor_name(), map()) ::
  PostHog.API.Client.response() | {:error, PostHog.Error.t()}
```

Make request to [`/flags`](https://posthog.com/docs/api/flags) API.

This function is a thin wrapper over a client call and is useful as a building
block to build your own `check/3`. For example, this is a preferred
way to access remote config payload.

## Examples

Make request to `/flags` API:

    PostHog.FeatureFlags.flags(%{distinct_id: "user123"})

Make request to `/flags` API with additional body params:

    PostHog.FeatureFlags.flags(%{distinct_id: "my_distinct_id", groups: %{group_type: "group_id"}})

Make request to `/flags` API through a named PostHog instance:

    PostHog.FeatureFlags.flags(MyPostHog, %{distinct_id: "user123"})

# `flags_for`

```elixir
@spec flags_for(PostHog.supervisor_name(), PostHog.distinct_id() | map() | nil) ::
  {:ok, map()} | {:error, Exception.t()}
```

Get all feature flags.

Accepts an optional `distinct_id` or a map with request body. If neither is
passed, attempts to read `distinct_id` from the context.

## Examples

Get all feature flags:

    PostHog.FeatureFlags.flags_for("user123")

Get all feature flags with full request body:

    PostHog.FeatureFlags.flags_for(%{distinct_id: "user123", groups: %{group_type: "group_id"}})

Get all feature flags for `distinct_id` from the context:

    PostHog.set_context(%{distinct_id: "user123"})
    PostHog.FeatureFlags.flags_for()

Get all feature flags through a named PostHog instance:

    PostHog.FeatureFlags.flags_for(MyPostHog, "foo")

# `get_feature_flag_result`

> This function is deprecated. Use PostHog.FeatureFlags.evaluate_flags/2 with PostHog.FeatureFlags.Evaluations.

```elixir
@spec get_feature_flag_result(
  PostHog.supervisor_name(),
  String.t(),
  PostHog.distinct_id() | map() | nil,
  keyword()
) :: {:ok, PostHog.FeatureFlags.Result.t() | nil} | {:error, Exception.t()}
```

Gets the full feature flag result including value and payload.

> #### Deprecated {: .warning}
>
> Use `PostHog.FeatureFlags.evaluate_flags/2` and access flags from the
> returned `PostHog.FeatureFlags.Evaluations` snapshot. The snapshot
> exposes the same metadata (id, version, reason, payload) plus filter
> helpers and capture enrichment via `set_in_context/2`.

Returns `{:ok, %PostHog.FeatureFlags.Result{}}` on success, `{:ok, nil}` if the flag
is not found, or `{:error, reason}` on failure.

The `PostHog.FeatureFlags.Result` struct contains:
- `key` - The flag name
- `enabled` - Whether the flag is enabled
- `variant` - The variant string (nil for boolean flags)
- `payload` - The JSON payload configured for the flag (nil if not set)

By default, this function will
[send](https://posthog.com/docs/api/flags#step-3-send-a-feature_flag_called-event)
a `$feature_flag_called` event and
[set](https://posthog.com/docs/api/flags#step-2-include-feature-flag-information-when-capturing-events)
the `$feature/feature-flag-name` property in context.

## Options

- `:send_event` - Whether to send the `$feature_flag_called` event. Defaults to `true`.

## Examples

Get feature flag result for `distinct_id`:

    iex> PostHog.FeatureFlags.get_feature_flag_result("example-feature-flag-1", "user123")
    {:ok, %PostHog.FeatureFlags.Result{key: "example-feature-flag-1", enabled: true, variant: nil, payload: nil}}

Get feature flag result with payload:

    iex> PostHog.FeatureFlags.get_feature_flag_result("feature-with-payload", "user123")
    {:ok, %PostHog.FeatureFlags.Result{key: "feature-with-payload", enabled: true, variant: "variant1", payload: %{"key" => "value"}}}

Get feature flag result without sending event:

    iex> PostHog.FeatureFlags.get_feature_flag_result("my-flag", "user123", send_event: false)
    {:ok, %PostHog.FeatureFlags.Result{key: "my-flag", enabled: true, variant: nil, payload: nil}}

Flag not found returns `{:ok, nil}`:

    iex> PostHog.FeatureFlags.get_feature_flag_result("non-existent-flag", "user123")
    {:ok, nil}

Get feature flag result for `distinct_id` in the current context:

    iex> PostHog.set_context(%{distinct_id: "user123"})
    iex> PostHog.FeatureFlags.get_feature_flag_result("example-feature-flag-1")
    {:ok, %PostHog.FeatureFlags.Result{key: "example-feature-flag-1", enabled: true, variant: nil, payload: nil}}

Get feature flag result through a named PostHog instance:

    PostHog.FeatureFlags.get_feature_flag_result(MyPostHog, "example-feature-flag-1", "user123")

# `get_feature_flag_result!`

> This function is deprecated. Use PostHog.FeatureFlags.evaluate_flags/2 with PostHog.FeatureFlags.Evaluations.

```elixir
@spec get_feature_flag_result!(
  PostHog.supervisor_name(),
  String.t(),
  PostHog.distinct_id() | map() | nil,
  keyword()
) :: PostHog.FeatureFlags.Result.t() | nil | no_return()
```

Gets the full feature flag result or raises on error.

> #### Deprecated {: .warning}
>
> Use `PostHog.FeatureFlags.evaluate_flags/2` and access flags from the
> returned `PostHog.FeatureFlags.Evaluations` snapshot.

This is a wrapper around `get_feature_flag_result/4` that returns the result
directly or raises an exception on error. This follows the Elixir convention
where functions ending with `!` raise exceptions instead of returning error
tuples.

Returns `nil` if the flag is not found (does not raise), consistent with
other PostHog SDKs.

> **Warning**: Use this function with care as it will raise an error if there
> are any API errors (e.g. missing `distinct_id`). For more resilient code,
> use `get_feature_flag_result/4` which returns `{:error, reason}` instead of
> raising.

## Options

- `:send_event` - Whether to send the `$feature_flag_called` event. Defaults to `true`.

## Examples

Get feature flag result for `distinct_id`:

    iex> PostHog.FeatureFlags.get_feature_flag_result!("example-feature-flag-1", "user123")
    %PostHog.FeatureFlags.Result{key: "example-feature-flag-1", enabled: true, variant: nil, payload: nil}

Returns `nil` when flag is not found:

    iex> PostHog.FeatureFlags.get_feature_flag_result!("non-existent-flag", "user123")
    nil

Raises an error when `distinct_id` is missing:

    iex> PostHog.FeatureFlags.get_feature_flag_result!("example-feature-flag-1")
    ** (PostHog.Error) distinct_id is required but wasn't explicitly provided or found in the context

# `set_in_context`

```elixir
@spec set_in_context(PostHog.FeatureFlags.Evaluations.t()) :: :ok
```

Copies a snapshot's `$feature/<key>` and `$active_feature_flags` properties
into the default per-process PostHog context.

## Parameters

- `snapshot` - `t:PostHog.FeatureFlags.Evaluations.t/0` returned by
  `evaluate_flags/2` or one of the filtering helpers.

## Returns

Returns `:ok`.

## Remarks

Any subsequent `PostHog.capture/3` from this process automatically attaches
these properties to the captured event — no additional `/flags` request,
with the values guaranteed to match what the snapshot already evaluated.

For one-off enrichment without touching context, merge
`PostHog.FeatureFlags.Evaluations.event_properties/1` into a capture's
properties directly.

## Examples

    {:ok, snapshot} = PostHog.FeatureFlags.evaluate_flags("user123")
    PostHog.FeatureFlags.set_in_context(snapshot)

    # All subsequent captures pick up $feature/* and $active_feature_flags
    PostHog.capture("page_viewed", %{distinct_id: "user123"})

# `set_in_context`

```elixir
@spec set_in_context(PostHog.supervisor_name(), PostHog.FeatureFlags.Evaluations.t()) ::
  :ok
```

Copies a snapshot's `$feature/<key>` and `$active_feature_flags` properties
into a named PostHog instance's per-process context.

## Parameters

- `name` - supervisor name of the PostHog instance whose context should be
  updated.
- `snapshot` - `t:PostHog.FeatureFlags.Evaluations.t/0` returned by
  `evaluate_flags/2` or one of the filtering helpers.

## Returns

Returns `:ok`.

## Remarks

This is the named-instance variant of `set_in_context/1`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
