ServicesRemote Config

Feature Flags and Targeting

Attaching rules to a config turns it into a feature flag that /config/{key}/evaluate resolves per user. A rule set is an ordered array plus a default: rules are tried top to bottom, the first rule that produces a result wins, and if none does, default_enabled (and the optional default_variant) decide.

Rule types

Rules are tagged JSON objects; type selects the shape. Any rule may carry a variant payload, returned when that rule matches with enabled: true - that is the A/B test hook.

  • user_segment - conditions maps attribute names to expected values; all must match (an array value means any-of). Non-matching users fall through to the next rule.
  • percentage - value is 0-100. With sticky: true (the default) the bucket is a deterministic hash of config key + user id, so a user's answer never flaps between calls. A percentage rule is terminal: it always produces a result (match or no-match), so rules listed after it never run - put it last.
  • schedule - start_time/optional end_time. Inside the window the rule returns its enabled; outside it falls through. Verified live: an open window returned schedule_active, a future window fell through to the default.
  • user_list - explicit whitelist or blacklist of user_ids. Listed users get the configured result (inverted for blacklists); everyone else falls through.

Managing rules

snug remote-config set-rules -k feature.dark-mode \
  -r '[{"type":"user_segment","conditions":{"plan":"pro"},"enabled":true,"variant":{"theme":"midnight"}},
       {"type":"percentage","value":25}]'
snug remote-config get-rules -k feature.dark-mode
snug remote-config delete-rules -k feature.dark-mode      # admin only

The server assigns each rule an id (rule_NWZDfYWeMUcC in the live run) and echoes the stored set with default_enabled and updated_at. On set-rules, -d/--default-enabled is a boolean flag - not a description.

Environment-scoped rules shadow, they do not merge. set-rules -e staging stores a separate rule set for that environment, and once one exists the global rules are ignored entirely there. Reproduced live: with global rules matching plan=pro and a staging rule set containing only a whitelist, a pro user in staging fell through to default_disabled. get-rules -e falls back to the global set only when the environment has no rules of its own.

Evaluating

snug --output json remote-config evaluate -k feature.dark-mode -u user-1 --attr plan=pro
{
  "key": "docs4-rc.dark-mode",
  "enabled": true,
  "reason": "user_segment_match",
  "rule_id": "rule_NWZDfYWeMUcC",
  "variant": { "theme": "midnight" }
}

reason names the deciding path (user_segment_match, percentage_no_match, schedule_active, user_whitelist, default_disabled, no_rules, ...); the full list is in the API reference. Evaluation is open to any authenticated user - client apps can call it directly.

Over raw HTTP, user attributes are flattened query parameters, not a JSON blob:

GET /api/v1/config/{key}/evaluate?user_id=user-1&plan=pro

There is no attributes parameter - passing one just creates an attribute literally named attributes, which silently matches nothing.

Two verified requirements on user_id:

  • Omitting it entirely is a 400 with a plain-text body (Failed to deserialize query string: missing field 'user_id') - this rejection comes from query parsing and is not enveloped.
  • Passing it empty (user_id=) is an enveloped 400 { "msg": "user_id is required", "error": "BAD_REQUEST" }.

Attribute values are parsed as JSON when possible: --attr level=3 arrives as the number 3. Reproduced live: a segment condition of {"level":3} matched it, but {"level":"3"} (string) did not. Type your conditions to match what clients send.

Batch evaluation

snug --output json remote-config evaluate-batch -k feature.dark-mode,limits.rps \
  -u user-1 --attr plan=pro
{
  "evaluations": {
    "limits.rps": { "enabled": false, "reason": "no_rules" },
    "docs4-rc.dark-mode": {
      "enabled": true,
      "reason": "user_segment_match",
      "rule_id": "rule_NWZDfYWeMUcC",
      "variant": { "theme": "midnight" }
    }
  },
  "evaluation_time_ms": 2.070708
}

Keys are comma-separated in one -k, not repeated flags. Configs without rules evaluate to enabled: false with reason no_rules.

Analytics

Every evaluation is counted per key, day, and environment. Reading the counters requires a platform admin or editor token:

snug --output json remote-config analytics -k feature.dark-mode -w 2026-08-28
{
  "key": "docs4-rc.dark-mode",
  "window": "2026-08-28",
  "enabled_count": 3,
  "disabled_count": 3,
  "total_evaluations": 6,
  "unique_users": 2,
  "reasons": { "user_segment_match": 3, "percentage_no_match": 3 }
}

-w is a YYYY-MM-DD day window and defaults to today; -e scopes to an environment.

On this page